Android 多線程檔案下載

來源:互聯網
上載者:User

原理一:HTTP多線程下載原理

1、發送一個含有Rang頭的Head請求,如果返回狀態代碼為206,則允許多線程下載

 

原理二:多線程下載原理

1、使用HttpClient的Head請求擷取請求檔案的資訊

2、發送一個Rang的Head請求判斷是否允許多線程下載

3、通過主任務建立多個分段下載線程,分段下載檔案,然後用Java的隨機讀寫檔案類儲存下載的內容

發送器(有ProgressBar等待)

代碼都有注釋,很容易看懂

/** * 非同步檔案下載器,可開啟多線程進行下載 *  * @author zhihong.lanzh *  */public class FileDownloadAsyncTask extends AsyncTask<Void, Integer, Boolean> {private static String TAG = "FileDownloadAsyncTask";private int downloadedSize = 0;private int fileSize = 0;private int blockSize, downloadSizeMore;private int threadNum = 5;private String urlStr, fileName;private Context mContext;private ProgressDialog mProgressDialog;private Exception mException = null;private Callback<Boolean> pCallback;private Callback<Exception> pExceptionCallback;private HttpClient httpClient;private boolean acceptRanges = false;// 是否支援多線程下載public FileDownloadAsyncTask(Context context, String urlStr, int threadNum, String fileName, Callback<Boolean> pCallback) {httpClient = new DefaultHttpClient();this.urlStr = urlStr;if(threadNum < 1)this.threadNum = 1;elsethis.threadNum = threadNum;this.fileName = fileName;File file = new File(fileName);if (file.exists()) {file.delete();}this.mContext = context;this.pCallback = pCallback;this.pExceptionCallback = null;}public FileDownloadAsyncTask(Context context, String urlStr, int threadNum, String fileName, Callback<Boolean> pCallback,Callback<Exception> pExceptionCallback) {this(context, urlStr, threadNum, fileName, pCallback);this.pExceptionCallback = pExceptionCallback;}@Overrideprotected Boolean doInBackground(Void... params) {try {getDownloadFileInfo();Log.v(TAG, "fileSize:" + fileSize);if (fileSize == -1) {return false;}if (acceptRanges == false) {threadNum = 1;}FileDownloadThread[] fds = new FileDownloadThread[threadNum];// 計算每個線程要下載的資料量blockSize = fileSize / threadNum;// 解決整除後百分比計算誤差downloadSizeMore = (fileSize % threadNum);File file = new File(fileName);Log.v(TAG, "doInBackground2");for (int i = 0; i < threadNum; i++) {// 啟動線程,分別下載自己需要下載的部分FileDownloadThread fdt = new FileDownloadThread(httpClient, urlStr, file, i * blockSize, (i + 1) * blockSize - 1);fdt.setName("Thread" + i);fdt.start();fds[i] = fdt;}if (threadNum > 1) {// 啟動最後一個線程下載最後一部分FileDownloadThread fdt = new FileDownloadThread(httpClient, urlStr, file, (threadNum - 1) * blockSize, threadNum * blockSize - 1+ downloadSizeMore);fdt.setName("Thread" + (threadNum - 1));fdt.start();fds[(threadNum - 1)] = fdt;}boolean finished = false;while (!finished) {finished = true;downloadedSize = 0;for (int i = 0; i < fds.length; i++) {downloadedSize += fds[i].getDownloadSize();if (!fds[i].isFinished()) {finished = false;}Log.v(TAG, "fds[" + i + "]:" + fds[i].isFinished());}int progress = (int) (((double) downloadedSize / fileSize) * 100);onProgressUpdate(progress);Log.v(TAG, "downloadedSize:" + downloadedSize);// 休息200ms後再讀取下載進度// Thread.sleep(5);}boolean result = true;for (int i = 0; i < threadNum; i++) {result = result & fds[i].getResult();}Log.v(TAG, "Result:" + result);return result;} catch (Exception e) {e.printStackTrace();this.mException = e;return false;} finally {httpClient.getConnectionManager().shutdown();}}/** * 擷取檔案下載資訊 */private void getDownloadFileInfo() {try {HttpHead httpHead = new HttpHead(urlStr);HttpResponse response = httpClient.execute(httpHead);if (response.getStatusLine().getStatusCode() != 200) {fileSize = -1;}Log.v(TAG, "response");// 擷取下載檔案的總大小// fileSize = (int) response.getEntity().getContentLength();Header[] headers = response.getHeaders("Content-Length");if (headers.length > 0)fileSize = Integer.parseInt(headers[0].getValue());httpHead.abort();if (threadNum != 1) {// 判斷是否允許多線程下載httpHead = new HttpHead(urlStr);httpHead.addHeader("Range", "bytes=0-" + (fileSize - 1));response = httpClient.execute(httpHead);if (response.getStatusLine().getStatusCode() == 206) {acceptRanges = true;}Log.v(TAG, "acceptRanges:" + response.getStatusLine().getStatusCode());httpHead.abort();}} catch (ClientProtocolException e) {fileSize = -1;Log.e(TAG, e.getMessage());// e.printStackTrace();} catch (IOException e) {fileSize = -1;Log.e(TAG, e.getMessage());// e.printStackTrace();}}@Overrideprotected void onPreExecute() {this.mProgressDialog = new ProgressDialog(mContext);this.mProgressDialog.setTitle(R.string.update_dialog_download_title);this.mProgressDialog.setIcon(android.R.drawable.ic_menu_save);this.mProgressDialog.setIndeterminate(false);this.mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);this.mProgressDialog.setMax(100);this.mProgressDialog.show();super.onPreExecute();}@Overrideprotected void onPostExecute(Boolean result) {try {if (result != null)this.mProgressDialog.dismiss();} catch (final Exception e) {Log.e("Error", e.getLocalizedMessage());/* Nothing. */}if (this.isCancelled()) {this.mException = new CancelledException();}if (this.mException == null) {pCallback.onCallback(result);} else {if (pExceptionCallback != null) {pExceptionCallback.onCallback(this.mException);}}super.onPostExecute(result);}@Overrideprotected void onProgressUpdate(Integer... values) {this.mProgressDialog.setProgress(values[0]);super.onProgressUpdate(values);}}

各個線程的下載片段

/** * 檔案下載線程 * @author zhihong.lanzh * */public class FileDownloadThread extends Thread {private static final String TAG = "FileDownloadThread";private static final int BUFFER_SIZE = 1024;private String url;private File file;private int startPosition;private int endPosition;private int curPosition;// 用於標識當前線程是否下載完成private boolean finished = false;private boolean result = false;private int downloadSize = 0;private Object o = new Object();private HttpClient httpClient;public FileDownloadThread(HttpClient httpClient , String url, File file, int startPosition, int endPosition) {this.httpClient = httpClient;this.url = url;this.file = file;this.startPosition = startPosition;this.curPosition = startPosition;this.endPosition = endPosition;}@Overridepublic void run() {if(httpClient == null){this.finished = true;this.result = false;return;}BufferedInputStream bis = null;RandomAccessFile fos = null;byte[] buf = new byte[BUFFER_SIZE];try {HttpGet httpGet = new HttpGet(url);HttpResponse response = httpClient.execute(httpGet);if(response.getStatusLine().getStatusCode()  != 200){this.finished = true;this.result = false;return;}HttpEntity entity = response.getEntity();// 設定當前線程下載的起點,終點httpGet.addHeader("Range", "bytes=" + startPosition + "-" + endPosition);// 使用java中的RandomAccessFile 對檔案進行隨機讀寫操作fos = new RandomAccessFile(file, "rw");// 設定開始寫檔案的位置fos.seek(startPosition);bis = new BufferedInputStream(entity.getContent());// 開始迴圈以流的形式讀寫檔案while (curPosition < endPosition) {int len = bis.read(buf, 0, BUFFER_SIZE);if (len == -1) {break;}fos.write(buf, 0, len);curPosition = curPosition + len;synchronized (o) {if (curPosition > endPosition) {downloadSize += len - (curPosition - endPosition) + 1;} else {downloadSize += len;}}//Log.v(TAG, "curPosition:" + curPosition + ",endPosition:" + endPosition  + ",downloadSize:" + downloadSize);//sleep(100);}// 下載完成設為truethis.finished = true;this.result = true;bis.close();fos.close();httpGet.abort();} catch (Exception e) {result = false;e.printStackTrace();Log.v(TAG, e.getMessage());}}public boolean isFinished() {return finished;}public int getDownloadSize() {synchronized (o) {return downloadSize;}}public boolean getResult() {return result;}public void setResult(boolean result) {this.result = result;}}

 
    

Reference:

http://zywang.iteye.com/blog/916489

http://blog.csdn.net/goodname008/article/details/568668

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

Tags Index: