/** * 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(); } } } }