標籤:
Apache HttpClient架構
GET方式請求提交資料
1. 建立一個HttpClient
HttpClient hc = new DefaultHttpClient();
2. 建立一個HttpGet,要提交給伺服器的資料已經拼接在path中
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則說明請求成功
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();//請求部分資料,成功的響應碼是206if(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();
手機版的斷點續傳多線程下載器
用進度條顯示下載進度
拿到下載檔案總大小時,設定進度條的最大值
pb.setMax(length); // 設定進度條的最大值
進度條需要顯示所有線程的整體下載進度,所以各條線程每下載一次,就要把新下載的長度加入進度條
每次斷點下載時,從新的開始位置開始下載,進度條也要從新的位置開始顯示,在讀取快取檔案擷取新的下載開始位置時,也要處理進度條進度
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模組支援斷點續傳,隨時停止下載任務,開始任務
建立HttpUtils對象
HttpUtils http = new HttpUtils();
下載檔案
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 + "%");// 文本進度 }});
Android應用開發-網路編程(二)(重製版)