Android模拟多线程下载
生活随笔
收集整理的這篇文章主要介紹了
Android模拟多线程下载
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
本DEMO采用Executor框架來實(shí)現(xiàn)多線程的下載。
Executor原理:任務(wù)拆分為一些列的小任務(wù),即Runnable,然后在提交給一個(gè)Executor執(zhí)行,Executor.execute(Runnalbe) 。Executor在執(zhí)行時(shí)使用內(nèi)部的線程池完成操作。
本博文演示的從服務(wù)端多線程下載圖片主要是通過HTTP請(qǐng)求頭的Range,在線程池中初始化線程數(shù),然后根據(jù)算法去計(jì)算,每個(gè)線程去下載指定Range范圍的資源,每個(gè)線程現(xiàn)在完成后發(fā)送Message消息給主線程的handler ,當(dāng)所有的線程都下載完成后,handler主動(dòng)更新主線程UI。
詳細(xì)代碼請(qǐng)移步本人GITHUB
客戶端核心代碼
package com.turing.base.http.downHttp;import android.os.Environment; import android.os.Handler; import android.os.Message;import com.apkfuns.logutils.LogUtils;import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.Executor; import java.util.concurrent.Executors;/*** MyApp** @author Mr.Yang on 2015-11-18 18:58.* @version 1.0* 多線程下載的實(shí)現(xiàn)過程:* 1、首先得到下載文件的長(zhǎng)度,然后設(shè)置本地文件* 的長(zhǎng)度。* HttpURLConnection.getContentLength();* RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");* file.setLength(filesize);//設(shè)置本地文件的長(zhǎng)度* 2、根據(jù)文件長(zhǎng)度和線程數(shù)計(jì)算每條線程下載的數(shù)據(jù)長(zhǎng)度和下載位置。* 如:文件的長(zhǎng)度為6M,線程數(shù)為3,那么,每條線程下載的數(shù)據(jù)長(zhǎng)度為2M,每條線程開始下載的位置如上圖所示。* 3、使用Http的Range頭字段指定每條線程從文件的什么位置開始下載,下載到什么位置為止,* 如:指定從文件的2M位置開始下載,下載到位置(4M-1byte)為止,代碼如下:* con.setRequestProperty("Range", "bytes=" + 開始位置+ "-" + "結(jié)束位置");* HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");* 4、保存文件,使用RandomAccessFile類指定每條線程從本地文件的什么位置開始寫入數(shù)據(jù)。* RandomAccessFile threadfile = newRandomAccessFile("QQWubiSetup.exe ","rwd");* threadfile.seek(2097152);//從文件的什么位置開始寫入數(shù)據(jù)*/ public class DownLoad {// 創(chuàng)建線程池,指定大小private Executor threadPool = Executors.newFixedThreadPool(3);public void downLoadFile(String url, Handler handler) {try {// 根據(jù)URL實(shí)例化URLURL httpUrl = new URL(url);// 打開連接HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();// 設(shè)置屬性conn.setRequestMethod("GET");conn.setReadTimeout(1000 * 5);// 因?yàn)橐ㄟ^線程池-多線程下載文件,所以應(yīng)該先獲取總長(zhǎng)度,然后分配每個(gè)線程的下載大小int count = conn.getContentLength();// 假設(shè)通過三個(gè)線程下載,計(jì)算每個(gè)線程的下載大小int block = count / 3;// 遍歷 計(jì)算出每個(gè)線程獲取內(nèi)容的起始位置// 獲取文件名稱String fileName = getFileName(url);LogUtils.d("fileName:" + fileName);// 指定文件的下載地址,加載到sd卡中File parent = Environment.getExternalStorageDirectory();File downFile = new File(parent, fileName);for (int i = 0; i < 3; i++) {// 算法解釋 假設(shè)文件的大小為11個(gè)字節(jié),我們通過線程池開啟3個(gè)線程,這樣還剩余2個(gè)字節(jié),在最后的線程里處理// 第一個(gè)線程 從0開始獲取到2// 第二個(gè)線程 從3開始獲取到5// 第三個(gè)線程 從6到10long start = i * block;long end = (i + 1) * block - 1;if (i == 2) {end = count;}LogUtils.d("start:" + start + ",end" + end);// 實(shí)例化Runnable對(duì)象LogUtils.d("getAbsolutePath:" + downFile.getAbsolutePath());DownLoadRunnnable downLoadRunnnable = new DownLoadRunnnable(url, downFile.getAbsolutePath(), start, end, handler);// 提交到線程池threadPool.execute(downLoadRunnnable);LogUtils.d("execute成功");}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 根據(jù)URL的后綴 獲取要獲取的圖片文件名稱* http://localhost:8080/HttpService/girl.jpg*/public String getFileName(String url) {// 獲取最后一個(gè)/ 截取文件名稱return url.substring(url.lastIndexOf("/") + 1);}/*** DownLoadRunnnable對(duì)象*/static class DownLoadRunnnable implements Runnable {private String url;private String fileName;private long start;private long end;private Handler handler;// 構(gòu)造函數(shù)public DownLoadRunnnable(String url, String fileName, long start, long end, Handler handler) {this.url = url;this.fileName = fileName;this.start = start;this.end = end;this.handler = handler;}@Overridepublic void run() {try {URL downLoadURL = new URL(url);HttpURLConnection conn = (HttpURLConnection) downLoadURL.openConnection();conn.setReadTimeout(1000 * 5);conn.setRequestMethod("GET");//使用Http的Range頭字段指定每條線程從文件的什么位置開始下載,下載到什么位置為止,這里指定從開始到結(jié)束conn.setRequestProperty("Range", "bytes=" + start + "-" + end);// 保存文件/**public RandomAccessFile(File file, String mode) throws FileNotFoundException創(chuàng)建從中讀取和向其中寫入(可選)的隨機(jī)訪問文件流,該文件由 File 參數(shù)指定。將創(chuàng)建一個(gè)新的 FileDescriptor 對(duì)象來表示此文件的連接。mode 參數(shù)指定用以打開文件的訪問模式。允許的值及其含意為:值 含意"r" 以只讀方式打開。調(diào)用結(jié)果對(duì)象的任何 write 方法都將導(dǎo)致拋出 IOException。"rw" 打開以便讀取和寫入。如果該文件尚不存在,則嘗試創(chuàng)建該文件。"rws" 打開以便讀取和寫入,對(duì)于 "rw",還要求對(duì)文件的內(nèi)容或元數(shù)據(jù)的每個(gè)更新都同步寫入到底層存儲(chǔ)設(shè)備。"rwd" 打開以便讀取和寫入,對(duì)于 "rw",還要求對(duì)文件內(nèi)容的每個(gè)更新都同步寫入到底層存儲(chǔ)設(shè)備。**/RandomAccessFile accessFile = new RandomAccessFile(new File(fileName), "rwd");//從文件的什么位置開始寫入數(shù)據(jù)accessFile.seek(start);// 獲取輸入流InputStream ins = conn.getInputStream();// 設(shè)置緩沖區(qū)域byte[] b = new byte[1024 * 4];int len;// 循環(huán)讀取寫入accessFilewhile ((len = ins.read(b)) != -1) {accessFile.write(b, 0, len);}LogUtils.d("write 成功");// 關(guān)閉流if (accessFile != null) {accessFile.close();}if (ins != null) {ins.close();}// 發(fā)送消息通知主線程完成Message message = new Message();message.what = 1 ;handler.sendMessage(message);LogUtils.d("message發(fā)送成功");} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}}總結(jié)
以上是生活随笔為你收集整理的Android模拟多线程下载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android-上传图片(二)_Http
- 下一篇: Imageloader1-总体简介