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); }
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框架是一個異步的HttpClient框架,不須要咱們本身建立子線程,框架會爲咱們建立子線程去執行網絡的交互異步
1.建立異步HttpClient
AsyncHttpClient ahc = new AsyncHttpClient();
2. 發送GET請求提交數據,提交的數據拼接在path上
ahc.get(path, new MyResponseHandler());
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());
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分配給每一個線程的時間片相同,服務器帶寬平均分配給每一個線程,因此客戶端開啓的線程越多,就能搶佔到更多的服務器資源。實際上並非客戶端併發的下載線程越多,程序的下載速度就越快,由於當客戶端開啓太多的併發線程以後,應用程序須要維護每條線程的開銷、線程同步的開銷,這些開銷反而會致使下載速度下降;而且不管開多少個線程搶佔服務器資源,下載帶寬也不會超過客戶端的物理帶寬
獲取資源文件總大小,而後建立大小一致的臨時文件
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(); }
請求本身負責的那部分數據,同步寫入到臨時文件相應的位置
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是基於原來的開源框架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 + "%");// 文本進度 } });