Android 應(yīng)用開發(fā)】Android 平臺 HTTP網(wǎng)速測試 案例 API 分析

      網(wǎng)友投稿 828 2022-05-29

      轉(zhuǎn)載請注明出處 :?http://blog.csdn.net/shulianghan/article/details/25996817

      工信部規(guī)定的網(wǎng)速測試標(biāo)準(zhǔn) :?除普通網(wǎng)頁測速采用單線程外,用戶寬帶接入速率測試應(yīng)使用多線程(多TCP連接)HTTP下載進(jìn)行測速,測試中使用的線程數(shù)量為N(N≥4)。

      -- 建立連接 : 用戶終端設(shè)備發(fā)起測試請求后,與測速平臺建立 N 條 TCP 連接,并在每一條 TCP 連接上發(fā)送HTTP[GET]請求發(fā)起一次測試過程。

      -- 請求文件 : 對每一個 HTTP[GET]請求,寬帶接入速率測試平臺以 HTTP 200 OK 響應(yīng),并開始傳送測速文件。

      -- 下載文件 : 對每一條連接,寬帶接入速率測試平臺持續(xù)從內(nèi)存直接發(fā)送 64kByte 大小的內(nèi)容。

      -- 平均速率 : 從收到第 1 個 HTTP[GET]請求開始計時,寬帶接入速率測試平臺及客戶端軟件每隔 1s 統(tǒng)計已經(jīng)發(fā)送的文件大小,計算數(shù)據(jù)平均傳送速率,并在網(wǎng)頁上或客戶端中實(shí)時更新。

      -- 實(shí)時速率 : 寬帶接入速率測試平臺同時計算每 1s 間隔內(nèi)的實(shí)時數(shù)據(jù)傳送速率。

      -- 測量時間 : 15s 后寬帶接入速率測試平臺停止發(fā)送數(shù)據(jù),計算第 5s 到第 15s 之間共計 10s 的平均速率及峰值速率,峰值速率為步驟 5)中的每秒實(shí)時速率的最大值.

      一. 網(wǎng)速測試核心代碼

      從GitHub上下載的源碼, 應(yīng)該沒有按照工信部的標(biāo)準(zhǔn)寫的;

      在 GitHub 上找到的網(wǎng)速測試的核心代碼 :

      -- GitHub 地址 :?https://github.com/Mobiperf/Speedometer.git ;

      /** Runs the HTTP measurement task. Will acquire power lock to ensure wifi is not turned off */

      @Override

      public MeasurementResult call() throws MeasurementError {

      int statusCode = HttpTask.DEFAULT_STATUS_CODE;

      long duration = 0;

      long originalHeadersLen = 0;

      long originalBodyLen;

      String headers = null;

      ByteBuffer body = ByteBuffer.allocate(HttpTask.MAX_BODY_SIZE_TO_UPLOAD);

      boolean success = false;

      String errorMsg = "";

      InputStream inputStream = null;

      try {

      // set the download URL, a URL that points to a file on the Internet

      // this is the file to be downloaded

      HttpDesc task = (HttpDesc) this.measurementDesc;

      String urlStr = task.url;

      // TODO(Wenjie): Need to set timeout for the HTTP methods

      httpClient = AndroidHttpClient.newInstance(Util.prepareUserAgent(this.parent));

      HttpRequestBase request = null;

      if (task.method.compareToIgnoreCase("head") == 0) {

      request = new HttpHead(urlStr);

      } else if (task.method.compareToIgnoreCase("get") == 0) {

      request = new HttpGet(urlStr);

      } else if (task.method.compareToIgnoreCase("post") == 0) {

      request = new HttpPost(urlStr);

      HttpPost postRequest = (HttpPost) request;

      postRequest.setEntity(new StringEntity(task.body));

      } else {

      // Use GET by default

      request = new HttpGet(urlStr);

      }

      if (task.headers != null && task.headers.trim().length() > 0) {

      for (String headerLine : task.headers.split("\r\n")) {

      String tokens[] = headerLine.split(":");

      if (tokens.length == 2) {

      request.addHeader(tokens[0], tokens[1]);

      } else {

      throw new MeasurementError("Incorrect header line: " + headerLine);

      }

      }

      }

      byte[] readBuffer = new byte[HttpTask.READ_BUFFER_SIZE];

      int readLen;

      int totalBodyLen = 0;

      long startTime = System.currentTimeMillis();

      HttpResponse response = httpClient.execute(request);

      /* TODO(Wenjie): HttpClient does not automatically handle the following codes

      * 301 Moved Permanently. HttpStatus.SC_MOVED_PERMANENTLY

      * 302 Moved Temporarily. HttpStatus.SC_MOVED_TEMPORARILY

      * 303 See Other. HttpStatus.SC_SEE_OTHER

      * 307 Temporary Redirect. HttpStatus.SC_TEMPORARY_REDIRECT

      *

      * We may want to fetch instead from the redirected page.

      */

      StatusLine statusLine = response.getStatusLine();

      if (statusLine != null) {

      statusCode = statusLine.getStatusCode();

      success = (statusCode == 200);

      }

      /* For HttpClient to work properly, we still want to consume the entire response even if

      * the status code is not 200

      */

      HttpEntity responseEntity = response.getEntity();

      originalBodyLen = responseEntity.getContentLength();

      long expectedResponseLen = HttpTask.MAX_HTTP_RESPONSE_SIZE;

      // getContentLength() returns negative number if body length is unknown

      if (originalBodyLen > 0) {

      expectedResponseLen = originalBodyLen;

      }

      if (responseEntity != null) {

      inputStream = responseEntity.getContent();

      while ((readLen = inputStream.read(readBuffer)) > 0

      && totalBodyLen <= HttpTask.MAX_HTTP_RESPONSE_SIZE) {

      totalBodyLen += readLen;

      // Fill in the body to report up to MAX_BODY_SIZE

      if (body.remaining() > 0) {

      int putLen = body.remaining() < readLen ? body.remaining() : readLen;

      body.put(readBuffer, 0, putLen);

      }

      this.progress = (int) (100 * totalBodyLen / expectedResponseLen);

      this.progress = Math.min(Config.MAX_PROGRESS_BAR_VALUE, progress);

      broadcastProgressForUser(this.progress);

      }

      duration = System.currentTimeMillis() - startTime;

      }

      Header[] responseHeaders = response.getAllHeaders();

      if (responseHeaders != null) {

      headers = "";

      for (Header hdr : responseHeaders) {

      /*

      * TODO(Wenjie): There can be preceding and trailing white spaces in

      * each header field. I cannot find internal methods that return the

      * number of bytes in a header. The solution here assumes the encoding

      * is one byte per character.

      */

      originalHeadersLen += hdr.toString().length();

      headers += hdr.toString() + "\r\n";

      }

      }

      PhoneUtils phoneUtils = PhoneUtils.getPhoneUtils();

      MeasurementResult result = new MeasurementResult(phoneUtils.getDeviceInfo().deviceId,

      phoneUtils.getDeviceProperty(), HttpTask.TYPE, System.currentTimeMillis() * 1000,

      success, this.measurementDesc);

      result.addResult("code", statusCode);

      if (success) {

      result.addResult("time_ms", duration);

      result.addResult("headers_len", originalHeadersLen);

      result.addResult("body_len", totalBodyLen);

      result.addResult("headers", headers);

      result.addResult("body", Base64.encodeToString(body.array(), Base64.DEFAULT));

      }

      Log.i(SpeedometerApp.TAG, MeasurementJsonConvertor.toJsonString(result));

      return result;

      } catch (MalformedURLException e) {

      errorMsg += e.getMessage() + "\n";

      Log.e(SpeedometerApp.TAG, e.getMessage());

      } catch (IOException e) {

      errorMsg += e.getMessage() + "\n";

      Log.e(SpeedometerApp.TAG, e.getMessage());

      } finally {

      if (inputStream != null) {

      try {

      inputStream.close();

      } catch (IOException e) {

      Log.e(SpeedometerApp.TAG, "Fails to close the input stream from the HTTP response");

      }

      }

      if (httpClient != null) {

      httpClient.close();

      }

      }

      throw new MeasurementError("Cannot get result from HTTP measurement because " +

      errorMsg);

      }

      二. 分析源碼中用到的 API

      1. HttpClient

      (1) HttpClient 接口

      接口介紹 : 這是一個 http 客戶端接口, 該接口中封裝了一系列的對象, 這些對象可以執(zhí)行 處理cookie 身份驗(yàn)證 連接管理等 http 請求; 線程安全的客戶端都是基于 該接口 的實(shí)現(xiàn)和配置的;

      接口方法 : 執(zhí)行 各種 HttpRequest, 獲取連接管理實(shí)例 , 獲取客戶端參數(shù);

      (2) AndroidHttpClient 類

      類介紹 : 該類實(shí)現(xiàn)了 HttpClient 接口; 該類的本質(zhì)是一個 DefaultHttpClient, 為Android 進(jìn)行一些合理的配置 和 注冊規(guī)范, 創(chuàng)建該類實(shí)例的時候 使用 newInstance(String) 方法;

      方法介紹 :

      execute(HttpUriRequest) :

      public HttpResponse execute (HttpUriRequest request)

      -- 返回值 : 返回 request 的 response, 返回的是一個最終回應(yīng), 不會返回中間結(jié)果;

      2. HttpUriRequest

      (1) HttpUriRequest 接口

      接口介紹 : 該接口實(shí)現(xiàn)了 HttpRequest 接口, 提供了方便的方法用于獲取 request 屬性, 例如 request的 uri 和 函數(shù)類型等;

      方法介紹 :

      -- 中斷執(zhí)行 : 中斷 HttpRequest 的 execute()方法執(zhí)行;

      -- 獲取uri : 獲取request請求的 uri;

      -- 獲取方法 : 獲取 request 請求的 方法, 例如 GET, POST, PUT 等;

      -- 查詢是否中斷 : 查詢是否執(zhí)行了 abort()方法;

      (2) HttpGet 類

      類介紹 : Http 的 get 方法, 請求獲取 uri 所標(biāo)識的資源;

      get方法 : 該方法會檢索 請求地址 識別出來所有信息, 如果請求地址 引用了一個值, 這個值需要計算獲得, 響應(yīng)時返回的實(shí)體對應(yīng)的是計算后的值;

      方法特性 : getMethods 默認(rèn)情況下會 遵循 http 服務(wù)器的重定向請求, 這個行為可以通過調(diào)用 setFollowRedirects(false) 關(guān)閉;

      (3) HttpPost 類

      類介紹 : Http 的 Post 方法, 用于請求在 uri 指定的資源后附加的新數(shù)據(jù);

      Post方法功能 :

      -- 注釋資源 : 給存在的資源添加注釋;

      -- 發(fā)送信息 : 向 公告牌, 新聞組, 郵件列表 等發(fā)送信息;

      -- 數(shù)據(jù)傳輸 : 如 表單提交到一個數(shù)據(jù)處理程序;

      -- 數(shù)據(jù)庫 : 通過一個附加操作 擴(kuò)展數(shù)據(jù)庫;

      (4) HttpHead 類

      類介紹 : HEAD 方法等價于 GET 方法, 除了在響應(yīng)中不能返回方法體;

      元信息 : HEAD 請求 與 GET 請求 的響應(yīng)的消息頭中的元信息是一樣的;

      方法作用 : 這個方法可以用來獲取 請求中的元信息, 而不會獲取 請求數(shù)據(jù);

      常用用途 : 檢驗(yàn)超文本的可用性, 可達(dá)性, 和最近的修改;

      3. HttpResponse

      (1) HttpResponse 接口

      接口介紹 : Http響應(yīng)接口, 所有類型 HTTP 響應(yīng)都應(yīng)該實(shí)現(xiàn)這個接口;

      方法介紹 :

      -- 獲取信息實(shí)體 : 如果有可能可以通過 setEntity()方法設(shè)置;

      public abstract HttpEntity getEntity ()

      public abstract Locale getLocale ()

      public abstract StatusLine getStatusLine ()

      -- 設(shè)置響應(yīng)環(huán)境 :

      -- 設(shè)置狀態(tài)行 :

      -- 設(shè)置原因短語 : 使用原因短語更新狀態(tài)行, 狀態(tài)行只能被更新, 不能顯示的設(shè)置 或者 在構(gòu)造方法中設(shè)置;

      public abstract void setReasonPhrase (String reason)

      public abstract void setStatusCode (int code)

      (2) BasicHttpResponse 類

      類介紹 : Http 響應(yīng)的基本實(shí)現(xiàn), 該實(shí)現(xiàn)可以被修改, 該實(shí)現(xiàn)確保狀態(tài)行的存在;

      方法介紹 : 該類 實(shí)現(xiàn)了 HttpResponse 接口, 實(shí)現(xiàn)了上述接口中的所有方法;

      4. StatusLine

      (1) StatusLine 接口

      接口介紹 : 該接口代表從 HTTP 服務(wù)器上返回的響應(yīng)的狀態(tài)行;

      方法介紹 :

      -- 獲取協(xié)議版本號 : getProtocalVersion();

      -- 獲取原因短語 : getReasonPhrase();

      -- 獲取狀態(tài)碼 : getStatusCode();

      (2) BasicStatusLine

      類介紹 : HTTP 服務(wù)器響應(yīng)的狀態(tài)行;

      方法介紹 : 實(shí)現(xiàn)了 StatusLine 的 3個 方法, 可以獲取 協(xié)議版本號, 原因短語, 狀態(tài)碼;

      5. HttpEntity 接口

      接口介紹 : HttpEntity 可以隨著 HTTP 消息發(fā)送和接收, 在一些 請求 和 響應(yīng)中可以找到 HttpEntity, 這是可選的;

      HttpEntity 分類 :

      -- 數(shù)據(jù)流 : 內(nèi)容是從數(shù)據(jù)流中獲取的, 或者是在內(nèi)存中生成的, 通常, 這類 實(shí)體是從連接中獲取的, 并且不可重復(fù);

      -- 獨(dú)立的 : 內(nèi)容從內(nèi)存中獲取, 或者從連接 或 其它 實(shí)體中獲取的, 可以重復(fù);

      【Android 應(yīng)用開發(fā)】Android 平臺 HTTP網(wǎng)速測試 案例 API 分析

      -- 包裝 : 從其它實(shí)體中獲取的;

      三. 網(wǎng)速測試流程

      a. 創(chuàng)建 AndroidHttpClient : 使用 AndroidHttpClient 的 newInstance(str)方法, 創(chuàng)建該實(shí)例, 創(chuàng)建實(shí)例的時候, 傳入的字符串是 包名 + 版本號, 自己組織;

      AndroidHttpClient httpClient = AndroidHttpClient.newInstance(packageName + " , " + version);

      HttpRequestBase request = null;

      if (task.method.compareToIgnoreCase("head") == 0) {

      request = new HttpHead(urlStr);

      } else if (task.method.compareToIgnoreCase("get") == 0) {

      request = new HttpGet(urlStr);

      } else if (task.method.compareToIgnoreCase("post") == 0) {

      request = new HttpPost(urlStr);

      HttpPost postRequest = (HttpPost) request;

      postRequest.setEntity(new StringEntity(task.body));

      } else {

      // Use GET by default

      request = new HttpGet(urlStr);

      }

      c. 創(chuàng)建緩沖區(qū)及相關(guān)數(shù)據(jù) : 創(chuàng)建一個 byte[] 緩沖區(qū), readLen 存儲當(dāng)前緩沖區(qū)讀取的數(shù)據(jù), totalBodyLen 存儲所有的下載的數(shù)據(jù)個數(shù);

      byte[] readBuffer = new byte[HttpTask.READ_BUFFER_SIZE];

      int readLen;

      int totalBodyLen = 0;

      HttpResponse response = httpClient.execute(request);

      StatusLine statusLine = response.getStatusLine();

      if (statusLine != null) {

      statusCode = statusLine.getStatusCode();

      success = (statusCode == 200);

      }

      HttpEntity responseEntity = response.getEntity();

      originalBodyLen = responseEntity.getContentLength();

      InputStream inputStream = responseEntity.getContent();

      readLen = inputStream.read(readBuffer)

      注意 : 網(wǎng)速測試時要避免與硬盤的操作, 因此不能將數(shù)據(jù)村到磁盤上, 只將數(shù)據(jù)存儲到內(nèi)存緩沖區(qū)中, 下一次緩沖區(qū)讀取的時候, 直接將上一次的緩沖區(qū)內(nèi)容覆蓋擦除;

      轉(zhuǎn)載請注明出處?:?http://blog.csdn.net/shulianghan/article/details/25996817

      Android API HTTP

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:AOS編排語言系列教程(七):創(chuàng)建負(fù)載均衡ELB
      下一篇:Kafka不只是個消息系統(tǒng)
      相關(guān)文章
      91亚洲一区二区在线观看不卡| 亚洲视频在线免费观看| 亚洲蜜芽在线精品一区| 亚洲AV无码成人网站久久精品大| 久久亚洲2019中文字幕| 亚洲精品老司机在线观看| 亚洲av区一区二区三| 亚洲国产精品日韩专区AV| 亚洲狠狠爱综合影院婷婷| 亚洲国产成人a精品不卡在线| 18禁亚洲深夜福利人口| 久久久久久亚洲精品无码| 日韩成人精品日本亚洲| 午夜亚洲国产成人不卡在线| 亚洲av无码国产精品色在线看不卡| 国产亚洲综合久久| 亚洲七七久久精品中文国产| 亚洲色偷偷综合亚洲AV伊人| 久久久久亚洲爆乳少妇无| 久久久久亚洲爆乳少妇无| 国产成人亚洲综合色影视| 亚洲成年人在线观看| 久久精品国产亚洲av日韩| 久久久亚洲欧洲日产国码二区 | 国产精品亚洲天堂| 日韩亚洲国产二区| 中文字幕中韩乱码亚洲大片| 亚洲精品成人片在线观看精品字幕| 亚洲va中文字幕无码久久| 亚洲人成电影在线天堂| 亚洲国产成人手机在线电影bd | 亚洲av之男人的天堂网站| 久久久亚洲裙底偷窥综合| 亚洲一级毛片免观看| 亚洲日韩AV一区二区三区中文| 久久精品国产亚洲av天美18| 亚洲综合亚洲综合网成人| 亚洲成亚洲乱码一二三四区软件| 亚洲av日韩综合一区在线观看| 亚洲毛片免费观看| 亚洲色丰满少妇高潮18p|