/**
* author : lipan
* filename : SpeedTestActivity.java
* create_time : 2014年11月11日 下午5:06
*/
package com.sets.speedtest.activity;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.apache.http.entity.StringEntity;
import org.json.JSONObject;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.sets.speedtest.R;
import com.sets.speedtest.common.CommDialog;
import com.sets.speedtest.common.CommLoading;
import com.sets.speedtest.common.CommToast;
import com.sets.speedtest.constant.C;
import com.sets.speedtest.domain.AddressInfo;
import com.sets.speedtest.domain.DeviceInfo;
import com.sets.speedtest.domain.TestLog;
import com.sets.speedtest.domain.UserInfo;
import com.sets.speedtest.listener.MyPhoneStateListener;
import com.sets.speedtest.listener.OnDialogClickListener;
import com.sets.speedtest.manager.SharedPreferencesManager;
import com.sets.speedtest.manager.http.BaseResponseHandler;
import com.sets.speedtest.manager.http.WSClient;
import com.sets.speedtest.service.UploadLogService;
import com.sets.speedtest.utils.CheckPhoneStatus;
import com.sets.speedtest.utils.CollectionUtils;
import com.sets.speedtest.utils.DateUtils;
import com.sets.speedtest.utils.ResponseParser;
import com.sets.speedtest.utils.StringB;
import com.sets.speedtest.utils.ViewUtils;
import com.wangjie.androidinject.annotation.annotations.base.AILayout;
import com.wangjie.androidinject.annotation.annotations.base.AIView;
/**
* @author : lipan
* @create_time : 2014年11月11日 下午5:07
* @desc : 测速界面
* @update_person:
* @update_time :
* @update_desc :
*
*/
@AILayout(R.layout.activity_speed_test)
public class SpeedTestActivity extends BaseActivity
{
@AIView(id = R.id.test_begin)
private Button testBtn; // 开始测试按钮
@AIView(id = R.id.comments)
private TextView commentsTextView;
@AIView(id = R.id.test_done)
private Button testDoneBtn;
private Boolean isDoing = false; // 是否正在测速
private DeviceInfo deviceInfo; // 设备信息
private SparseArray<TestLog> testArray; // 测试日志
private TelephonyManager telephonyManager;
private MyPhoneStateListener myListener; //
@Override
public void BtnClick(View v)
{
switch (v.getId())
{
case R.id.navi_btn_right: // 导航栏右上角
CommDialog.showConfirmDialog(getContext(), R.string.call_help,
R.string.call_help_confirm, getString(R.string.dialog_cancel),
getString(R.string.call_help_go), true, true, new OnDialogClickListener()
{
@Override
public void onRightClick(View v)
{
ViewUtils.call(getContext(), getString(R.string.call_help_no));
}
});
break;
case R.id.test_begin: // 开始测速按钮
if (!isDoing) // 如果正在测速,返回时给提示
{
testStart();
}
break;
case R.id.navi_left_button: // 返回按钮
case R.id.navi_left_button_layout: // 返回按钮
finishActivity();
break;
case R.id.test_done: // 完成测试
finishActivity();
break;
case R.id.test_close: // 完成测试
break;
default:
break;
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initData();
initView();
}
private void initView()
{
}
private void initData()
{
keepScreenOn();// 保持屏幕常亮
// 获得设备信息
deviceInfo = CheckPhoneStatus.getDeviceInfo(getApplicationContext());
/* 监控手机状态 */
myListener = new MyPhoneStateListener(getContext());
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(myListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
/**
* 开始测速
*/
private void testStart()
{
// WIFI下不能打开测速
if (CheckPhoneStatus.isWIFIConnection(getContext()))
{
CommDialog.showConfirmDialog(context, null, R.string.network_close_wifi, null,
context.getString(R.string.settings), false, false, new OnDialogClickListener()
{
@Override
public void onRightClick(View v)
{
if (android.os.Build.VERSION.SDK_INT > 10)
{
// 3.0以上打开设置界面,也可以直接用ACTION_WIRELESS_SETTINGS打开到wifi界面
context.startActivity(new Intent(
android.provider.Settings.ACTION_SETTINGS));
} else
{
context.startActivity(new Intent(
android.provider.Settings.ACTION_WIRELESS_SETTINGS));
}
}
});
} else
{
// 如果找不到下载url...重新获取一次
if (StringB.isBlank(SharedPreferencesManager.getInstance().getUserInfo().downloadUrl))
{
// 获得基本信息
WSClient.postJson(getContext(), C.WS_GET_BASE_INFO, null, new BaseResponseHandler(
getContext(), R.string.test_prepare, false)
{
@Override
public void onSuccess(int statusCode, JSONObject response)
{
ResponseParser handler = ResponseParser.handler(getApplicationContext(),
response);
if (handler.success)
{
SharedPreferencesManager.getInstance().setUploadUrl(
handler.data.optString("uploadUrl"));
SharedPreferencesManager.getInstance().setDownloadUrl(
handler.data.optString("downloadUrl"));
SharedPreferencesManager.getInstance().setDownloadUrlHost(
handler.data.optString("downloadUrlHost"));
SharedPreferencesManager.getInstance().setDownloadUrlPort(
handler.data.optString("downloadUrlPort"));
SharedPreferencesManager.getInstance().setDownloadUrlPath(
handler.data.optString("downloadUrlPath"));
SharedPreferencesManager.getInstance().setComments(
handler.data.optString("comments"));
SharedPreferencesManager.getInstance().setFormulaNum(
handler.data.optInt("formulaNum"));
isDoing = true; // 正在测速
CommLoading.showWithoutCancel(getContext(), R.string.test_doing); // loading
// 执行测速任务
new SpeedTestTask(SharedPreferencesManager.getInstance().getUserInfo())
.execute();
}
}
@Override
public void onFailure(Throwable error, String content)
{
testFaild();
}
});
}else
{
isDoing = true; // 正在测速
CommLoading.showWithoutCancel(getContext(), R.string.test_doing); // loading
// 执行测速任务
new SpeedTestTask(SharedPreferencesManager.getInstance().getUserInfo())
.execute();
}
}
}
/**
* 测速失败
*/
private void testFaild()
{
commentsTextView.setText(R.string.test_faild); // 提示内容
CommToast.showInfo(getContext(), R.string.test_faild);
}
/**
* 测试完成
*
* @param log
*
*/
private void testDone(TestLog log)
{
if (null == testArray)
{
testArray = new SparseArray<TestLog>();
}
testArray.append(1, log);
ViewUtils.hide(testBtn);// 隐藏开始测速按钮
ViewUtils.show(testDoneBtn);// 显式完成测试按钮组
// commentsTextView.setText(R.string.test_done); //测速完成提示
testCommit();
}
/**
* 提交测试结果
*/
private void testCommit()
{
// 带宽
int bandwidthAvg = testArray.get(1).download;
// 信号强度
int signalAvg = myListener.getSignalNum();
// 接入人数 = 带宽(KB) / 400 * X (这个X,开始时建议给小一点,如300,实际使用一段时间后再观察)
// 最大支持考试人数
Float examNum = ((float) bandwidthAvg / 400)
* SharedPreferencesManager.getInstance().getUserInfo().formulaNum;
final AddressInfo address = (AddressInfo) getParcel(AddressInfo.KEY);// 从parcel中取参数对象
address.setDownload(bandwidthAvg);
address.setSignalStrength(signalAvg);
address.setRecommendExamNum(examNum.intValue());
address.setTestTime(DateUtils.getCurrentMillis()); // 测试时间
try
{
// 上传测速结果
final JSONObject params = new JSONObject();
params.put("addressId", address.getAddressId());
if (CheckPhoneStatus.CARRIER_CLASS_CMC.equals(deviceInfo.carrier))
{
params.put("cmcNum", address.getDownload());
} else if (CheckPhoneStatus.CARRIER_CLASS_CUC.equals(deviceInfo.carrier))
{
params.put("cucNum", address.getDownload());
} else if (CheckPhoneStatus.CARRIER_CLASS_CTC.equals(deviceInfo.carrier))
{
params.put("ctcNum", address.getDownload());
}
WSClient.postJson(getContext(), C.WS_UPLOAD_RESULT, new StringEntity(params.toString(),
C.CHARSET_UTF8), new BaseResponseHandler(getContext(),
R.string.test_result_analysing, false)
{
@Override
public void onSuccess(int statusCode, JSONObject response)
{
ResponseParser handler = ResponseParser.handler(getContext(), response);
if (handler.success)
{
if (0 != handler.data.optInt("num"))
{
commentsTextView.setText(getString(R.string.test_upload_with_num,
deviceInfo.carrier_name, address.getDownload(),
handler.data.optInt("num")));
} else
{
if (CheckPhoneStatus.CARRIER_CLASS_CMC.equals(deviceInfo.carrier))// 移动提示联通未测试
{
commentsTextView.setText(getString(R.string.test_upload,
deviceInfo.carrier_name, address.getDownload(), "联通"));
} else if (CheckPhoneStatus.CARRIER_CLASS_CUC
.equals(deviceInfo.carrier))// 联通提示移动未测试
{
commentsTextView.setText(getString(R.string.test_upload,
deviceInfo.carrier_name, address.getDownload(), "移动"));
} else if (CheckPhoneStatus.CARRIER_CLASS_CUC
.equals(deviceInfo.carrier))// 联通提示移动未测试
{
commentsTextView.setText(getString(R.string.test_upload,
deviceInfo.carrier_name, address.getDownload(), "移动和联通"));
} else
{
commentsTextView.setText(getString(R.string.test_done,
address.getDownload()));
}
}
// 启动服务上传测速日志
Bundle data = new Bundle();
data.putParcelable(AddressInfo.KEY, address);
data.putParcelableArray(TestLog.KEY, new TestLog[] { testArray.get(1) });
startService(UploadLogService.class, data);
} else
{
CommToast.showInfo(getContext(), handler.errorMsg);
}
}
@Override
public void onFailure(Throwable error, String content)
{
CommToast.showInfo(getContext(), getString(R.string.system_error));
}
});
} catch (Exception e)
{
CommToast.showInfo(getContext(), getString(R.string.system_error));
}
}
/**
* 测速任务
*
*/
private class SpeedTestTask extends AsyncTask<Object, Object, Object>
{
private UserInfo userInfo;
private TestLog log;
private static final int parallels = 20; // TODO 并发20
public SpeedTestTask(UserInfo userInfo)
{
this.userInfo = userInfo;
}
@Override
protected void onPreExecute()
{
log = new TestLog();
log.begin_time = DateUtils.getCurrentMillis(); // 开始时间
}
@Override
protected Object doInBackground(Object... params)
{
boolean success = false;
try
{
showLogInfo(getActivity(), "准备测速...");
List<Long> logs = new LinkedList<Long>(); // 下载速度结果集合
Socket[] sockets = new Socket[parallels]; // Socket数组
InputStream[] ins = new InputStream[parallels];// Socket得到的输入流数组
String host = userInfo.downloadUrlHost; // Socket连接主机地址
int port = Integer.parseInt(userInfo.downloadUrlPort); // Socket连接端口
String path = userInfo.downloadUrlPath; // Socket连接路径
showLogInfo(getActivity(), "开启X个Socket连接...");
/**
* 1.开启X个Socket连接,设置所有Socket发送/接收缓冲大小为1024M,
* 接收X个Socket得到的输入流到一个数组中(设置Socket参数,避免缓冲区大小限制读取速度)
**/
for (int i = 0; i < parallels; i++)
{
sockets[i] = new Socket(host, port);
Socket s = sockets[i];
s.setReceiveBufferSize(1024000);
s.setSendBufferSize(1024000);
OutputStreamWriter osw = new OutputStreamWriter(s.getOutputStream());
StringBuffer sb = new StringBuffer();
sb.append("GET " + path + "?t=" + new Random().nextInt(1000) + " HTTP/1.1\r\n");
sb.append("Host: " + host + ":" + port + "\r\n");
sb.append("Connection: Keep-Alive\r\n");
sb.append("\r\n");
osw.write(sb.toString());
osw.flush();
ins[i] = s.getInputStream();
}
showLogInfo(getActivity(), "激活所有输入流...");
/** 2.循环输入流数组,将每一个输入流都读一个字节 (激活所有输入流 / 激活下载动作) **/
for (int i = 0; i < parallels; i++)
{
InputStream in = ins[i];
in.read();
}
for (int i = 0; i < parallels; i++)
{
InputStream in = ins[i];
in.skip(in.available());
}
// 开始计时
long begin = System.currentTimeMillis();
// 下载总量
int total = 0;
showLogInfo(getActivity(), "采集下载速度...");
/** 3.每一秒采集一次下载的速度 = X个输入流读取的总量 除以 总时间(采集下载速度) **/
while (true)
{
Thread.sleep(1000);
int size = 0;
for (int i = 0; i < parallels; i++)
{
InputStream in = ins[i];
int available = in.available();
size += available;
in.skip(available);
}
if (size == 0 && total > 0) // 下载总量大于0,并且所有线程下载完毕,退出循环
break;
total += size;
logs.add(total / (System.currentTimeMillis() - begin));
showLogInfo(getActivity(),
"size=" + total + ", speed="
+ (total / (System.currentTimeMillis() - begin)));
}
for (int i = 0; i < parallels; i++)
{
sockets[i].close();
}
showLogInfo(getActivity(), "分析下载速度集合,得到最终下载速度...");
/** 4.采集的速度集合排序,忽略结果中最快的10%和最慢的30%,最后的结果取平均值 (计算平均下载速度) **/
// 排序
Collections.sort(logs);
// 忽略结果最快的10%和最慢的30%
if (logs.size() > 1)
{
logs = logs.subList(logs.size() * 30 / 100, logs.size() * 90 / 100);
}
// 求平均值
log.download = CollectionUtils.getLongListAvg(logs).intValue();
log.end_time = DateUtils.getCurrentMillis(); // 结束时间
success = true;
} catch (Exception e)
{
showLogError(getActivity(), "测速异常" + e.toString());
}
return success;
}
@Override
protected void onPostExecute(Object result)
{
isDoing = false; // 完毕测速
CommLoading.dismiss();
if ((Boolean) result) // 测试成功
{
testDone(log);
} else
{
testFaild();
}
}
}
}