Android應用開發-網絡編程(二)

 

Apache HttpClient框架

 

GET方式請求提交數據

  1. 建立一個HttpClientandroid

HttpClient hc = new DefaultHttpClient();

 

  2. 建立一個HttpGet,要提交給服務器的數據已經拼接在path中apache

HttpGet hg = new HttpGet(path);

 

  3. 使用HttpClient對象發送GET請求,創建鏈接,返回響應頭對象緩存

HttpResponse hr = hc.execute(hg);

 

  4. 拿到響應頭中的狀態行,獲取狀態碼,若是爲200則說明請求成功服務器

if(hr.getStatusLine().getStatusCode() == 200){
    // 拿到響應頭中實體裏的內容,其實就是服務器返回的輸入流
    InputStream is = hr.getEntity().getContent();
    String text = Utils.getTextFromStream(is);
}

 

 

POST方式請求提交數據

  1. 建立一個HttpClient網絡

HttpClient hc = new DefaultHttpClient();

 

  2. 建立一個HttpPost,構造方法的參數就是網址多線程

HttpPost hp = new HttpPost(path);

 

  3. 往HttpPost對象裏放入要提交給服務器的數據併發

// 要提交的數據以鍵值對的形式封裝在BasicNameValuePair對象中
BasicNameValuePair bnvp = new BasicNameValuePair("name", name);
BasicNameValuePair bnvp2 = new BasicNameValuePair("pass", pass);
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(bnvp);
parameters.add(bnvp2);
// 要提交的數據都已經在集合中了,把集合傳給實體對象,並指定進行URL編碼的碼錶
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "utf-8");
// 設置POST請求對象的實體,其實就是把要提交的數據封裝至POST請求的輸出流中
hp.setEntity(entity);

 

  4. 使用HttpClient對象發送POST請求,創建鏈接,返回響應頭對象框架

HttpResponse hr = hc.execute(hp);

 

  5. 拿到響應頭中的狀態行,獲取狀態碼,若是爲200則說明請求成功dom

if(hr.getStatusLine().getStatusCode() == 200){
    // 拿到響應頭中實體裏的內容,其實就是服務器返回的輸入流
    InputStream is = hr.getEntity().getContent();
    String text = Utils.getTextFromStream(is);
}

 

 

android-async-http框架(基於Apache HttpClient框架封裝)

 

android-async-http框架是一個異步的HttpClient框架,不須要咱們本身建立子線程,框架會爲咱們建立子線程去執行網絡的交互異步

 

GET方式請求提交數據

  1.建立異步HttpClient

AsyncHttpClient ahc = new AsyncHttpClient();

 

  2. 發送GET請求提交數據,提交的數據拼接在path上

ahc.get(path, new MyResponseHandler());

 

 

POST方式請求提交數據

  1. 建立異步HttpClient

AsyncHttpClient ahc = new AsyncHttpClient();

 

  2. 把要提交的數據封裝至RequestParams

RequestParams params = new RequestParams();
params.add("name", name);
params.add("pass", pass);

 

  3. 發送POST請求提交數據

ahc.post(path, params, new MyResponseHandler());

 

 

響應處理器AsyncHttpResponseHandler

 

class MyResponseHandler extends AsyncHttpResponseHandler{

        // 請求服務器成功時(響應碼是200)回調此方法
        @Override
        public void onSuccess(int statusCode, Header[] headers,
                byte[] responseBody) {
            // responseBody的內容就是服務器返回的數據
            Toast.makeText(MainActivity.this, new String(responseBody,"GBK"), Toast.LENGTH_SHORT).show();
            
        }

        // Http請求失敗(返回碼不爲200),系統回調此方法
        @Override
        public void onFailure(int statusCode, Header[] headers,
                byte[] responseBody, Throwable error) {
            Toast.makeText(MainActivity.this, new String(responseBody,"GBK"), Toast.LENGTH_SHORT).show();
            
        }
    
    }

 

 

多線程下載

 

服務器CPU分配給每一個線程的時間片相同,服務器帶寬平均分配給每一個線程,因此客戶端開啓的線程越多,就能搶佔到更多的服務器資源。實際上並非客戶端併發的下載線程越多,程序的下載速度就越快,由於當客戶端開啓太多的併發線程以後,應用程序須要維護每條線程的開銷、線程同步的開銷,這些開銷反而會致使下載速度下降;而且不管開多少個線程搶佔服務器資源,下載帶寬也不會超過客戶端的物理帶寬

 

主線程首先發送Http GET請求肯定每一個線程下載哪部分數據

  • 獲取資源文件總大小,而後建立大小一致的臨時文件

    String path = "http://dldir1.qq.com/music/clntupate/QQMusic.apk";
    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5000);
    conn.setReadTimeout(5000);
    if(conn.getResponseCode() == 200){
        int length = conn.getContentLength();  // 得到服務器流中數據的長度
        RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd");// 建立一個臨時文件存儲下載的數據
        raf.setLength(length);    // 設置臨時文件的大小
        raf.close();

     

 

  • 計算每一個線程下載多少數據

    int blockSize = length / THREAD_COUNT;

     

  • 計算每一個線程下載數據的開始位置和結束位置,而後開啓下載子線程

    for(int id = 0; id < threadCount; id++){
        // 計算每一個線程下載數據的開始位置和結束位置
        int startIndex = id * blockSize;
        int endIndex = (id+1) * blockSize - 1;
        // 若是是最後一個線程,結束位置爲資源文件的結尾
        if(id == threadCount - 1){
            endIndex = length - 1;
        }           
        // 開啓線程,按照計算出來的開始結束位置開始下載數據
        new DownLoadThread(startIndex, endIndex, id).start();
    }

     

 

每一個下載線程再次發送Http GET請求,請求本身負責下載的那部分數據

  • 請求本身負責的那部分數據,同步寫入到臨時文件相應的位置

    String path = "http://dldir1.qq.com/music/clntupate/QQMusic.apk";
    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setConnectTimeout(5000);
    conn.setReadTimeout(5000);
    conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);// 設置本次Http請求的數據區間
    conn.connect();
    //請求部分數據,成功的響應碼是206
    if(conn.getResponseCode() == 206){
        InputStream is = conn.getInputStream();    // 流裏此時只有1/ThreadCount資源文件裏的數據
        RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd");
        raf.seek(startIndex);    // 把文件的寫入位置移動至startIndex
        byte[] b = new byte[1024];
        int len;
        while((len = is.read(b)) != -1){
            raf.write(b, 0, len);
        }
        raf.close();
    }

     

 

帶斷點續傳的多線程下載

 

  • 每一個下載線程定義了一個threadProgress成員變量記錄當前線程下載的進度,線程在往資源臨時文件中寫入數據的同時記錄下threadProgress,並存入進度緩存文件

    while((len = is.read(b)) != -1){
        raf.write(b, 0, len);
        threadProgress += len;
        RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd");
        progressRaf.write((threadProgress + "").getBytes());
        progressRaf.close();
    }

     

  • 下次下載開始時,先讀取緩存文件中的值,獲得的值就是該線程新的開始位置

    FileInputStream fis = new FileInputStream(progressFile);
    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
    threadProgress = Integer.parseInt(br.readLine());// 從進度臨時文件中讀取出上一次下載的總進度
    startIndex += threadProgress;         // 與本來的開始位置相加,獲得新的開始位置,完成斷點續傳
    fis.close();

 

  • 全部線程都下載完畢以後,刪除緩存文件

    finishedThread++;
    if(finishedThread == threadCount){
        for(int i = 0; i < threadCount; i++){
            File f = new File(target, i + ".txt");
            f.delete();
        }
    }

     

 

手機版的斷點續傳多線程下載器

 

用進度條顯示下載進度

  • 拿到下載文件總大小時,設置進度條的最大值

    pb.setMax(length);    // 設置進度條的最大值

     

  • 進度條須要顯示全部線程的總體下載進度,因此各條線程每下載一次,就要把新下載的長度加入進度條

    • 定義一個int全局變量,記錄三條線程的總下載長度

      int totalProgress;

       

    • 刷新進度條

      while((len = is.read(b)) != -1){
          raf.write(b, 0, len);
          // 每次讀取流裏數據以後,把每次讀取數據的長度顯示至進度條
          totalProgress += len;
          pb.setProgress(totalProgress);

       

  • 每次斷點下載時,重新的開始位置開始下載,進度條也要重新的位置開始顯示,在讀取緩存文件獲取新的下載開始位置時,也要處理進度條進度

    FileInputStream fis = new FileInputStream(progressFile);
    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
    // 從進度臨時文件中讀取出上一次下載的總進度
    // 而後與本來的開始位置相加,獲得新的開始位置,完成斷點續傳
    threadProgress = Integer.parseInt(br.readLine());
    startIndex += threadProgress;
    // 把上次多線程下載的總進度顯示至進度條
    totalProgress += threadProgress;
    pb.setProgress(totalProgress);

     

 

添加文本框顯示百分比進度

tv.setText((long) pb.getProgress() * 100 / pb.getMax() + "%");// 文本進度與進度條是同步的,轉換成long類型防止整型溢出

 

 

使用開源框架xUtils下載文件

 

開源框架xUtils是基於原來的開源框架afinal開發的,主要有四大模塊,其中HttpUtils模塊支持斷點續傳,隨時中止下載任務,開始任務

 

  1. 建立HttpUtils對象

    HttpUtils http = new HttpUtils();

     

  2. 下載文件

    http.download(path,     // 下載地址
            target,         // 下載數據保存的路徑和文件名
            true,           // 是否支持斷點續傳
            true, // 若是請求地址中沒有文件名,則文件名在響應頭中,下載完成後自動重命名
            new RequestCallBack<File>() {// 偵聽下載狀態
    
        // 下載成功後回調
        @Override
        public void onSuccess(ResponseInfo<File> responseInfo) {
            Toast.makeText(MainActivity.this, responseInfo.result.getPath(), Toast.LENGTH_SHORT).show();
        }
    
        // 下載失敗時回調,好比文件已經下載、沒有網絡權限、文件訪問不到,方法傳入一個字符串s告知失敗緣由
        @Override
        public void onFailure(HttpException e, String s) {
            tv_failure.setText(s);
        }
    
        // 在下載過程當中不斷的調用,用於刷新進度條
        @Override
        public void onLoading(long total, long current, boolean isUploading) {
            super.onLoading(total, current, isUploading);
            pb.setMax((int) total);      // 設置進度條總長度
            pb.setProgress((int) current);// 設置進度條當前進度
            tv_progress.setText(current * 100 / total + "%");// 文本進度
        }
    });
相關文章
相關標籤/搜索