android 多线程下载,断点续传,线程池
android 多线程下载,断点续传,线程池
你可以在这里看到这个demo的源码:
https://github.com/onlynight/MultiThreadDownloader
效果图
这张效果图是同时开启三个下载任务,限制下载线程数量的效果图。
多线程下载原理
多线程下载的原理就是将下载任务分割成一个个小片段再将每个小片段分配给各个线程进行下载。
例如一个文件大小为100M,我们决定使用4个线程下载,那么每个线程下载的大小即为25M,每个线程的起始以及结束位置依次如下:
0: 0-25M
1: 25-50M
2: 50-75M
3: 75-END
下载请求使用的是Http GET方法,GET请求允许我们设置参数请求的数据的范围:
HttpURLConnection conn = (HttpURLConnection)new URL("download url").openConnection();
// 设置http为get方法
conn.setRequestMethod("GET");
// 设置http请求的范围
conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
多线程访问文件需要创建RandamAccessFile:
RandomAccessFile threadFile = new RandomAccessFile(fileDownloader.getFileSavePath() + File.separator +fileDownloader.getFilename(), "rwd");
threadFile.seek(startPos);
断点续传原理
端点续传要求我们将之前下载过的文件片段保存下来,并且记录上次最后的下载位置。下次继续下载的时候从上次记录的位置开始下载即可,无需再从头下载。
线程池作用
线程池的原理分析和使用请查看者几篇文章:
http://blog.csdn.net/u010687392/article/details/49850803
http://blog.csdn.net/qq_17250009/article/details/50906508
试想如果我们有100个下载任务,我们让每个任务分成3个线程下载,那么每个任务都需要4个线程,如果100个任务同时开启下载那么就意味着需要同时启动400个线程执行下载任务,这样势必会影响app性能。
像上面这样大量的创建线程的操作势必会影响操作系统的性能,等这些任务执行完成后销毁线程同样也会消耗很多的系统资源,所以java中提出了线程池的概念。
下面我们来分析线程池是如何节约系统资源的。
从时间角度:
- 创线程的时间我们假定它为: t1
- 线程执行的时间我们假定为: t2
- 销毁线程的时间我们假定为: t3
就用上例我们计算其耗时为:
T = 100(t1+t2+t3)
我们使用固定上限线程数量的线程池耗时为:
T1 = 5(t1+t3)+100t2
显然固定线程上限线程池所需的时间短了很多,固定数量线程池节约了线程创建和销毁的时间,使用线程复用方法避免了线程的频繁创建和销毁,不仅节约了时间同时节约了系统资源。
关键代码
下载线程:
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;/*** Created by lion on 2017/2/10.*/public class DownloadRunnable implements Runnable {private int threadId = -1;private FileDownloader fileDownloader;private int downloadedSize = 0;private int startPos = -1;private int endPos = -1;private int downloadLength = 0;private boolean isFinish;private boolean isStart;public DownloadRunnable(FileDownloader fileDownloader, int threadId, int blockSize,int downloadedSize) {this.fileDownloader = fileDownloader;this.threadId = threadId;int fileSize = fileDownloader.getFileSize();this.startPos = blockSize * threadId + downloadedSize;this.endPos = blockSize * (threadId + 1) < fileSize ?blockSize * (threadId + 1) : fileSize;this.downloadedSize = downloadedSize;}@Overridepublic void run() {if (startPos >= endPos) {isFinish = true;} else {try {isStart = true;isFinish = false;HttpURLConnection conn = (HttpURLConnection)new URL(fileDownloader.getDownloadUrl()).openConnection();conn.setConnectTimeout(FileDownloader.getConnectionTimeOut());conn.setRequestMethod("GET");//set accept file meta-data typeconn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg," +" image/pjpeg, application/x-shockwave-flash, application/xaml+xml, " +"application/vnd.ms-xpsdocument, application/x-ms-xbap, " +"application/x-ms-application, application/vnd.ms-excel, " +"application/vnd.ms-powerpoint, application/msword, */*");conn.setRequestProperty("Accept-Language", "zh-CN");conn.setRequestProperty("Referer", fileDownloader.getDownloadUrl());conn.setRequestProperty("Charset", "UTF-8");conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; " +"Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; " +".NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");conn.setRequestProperty("Connection", "Keep-Alive");conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);conn.connect();RandomAccessFile threadFile = new RandomAccessFile(fileDownloader.getFileSavePath() + File.separator +fileDownloader.getFilename(), "rwd");threadFile.seek(startPos);InputStream inputStream = conn.getInputStream();byte[] buffer = new byte[10240];int offset;downloadLength = downloadedSize;while ((offset = inputStream.read(buffer, 0, 10240)) != -1) {threadFile.write(buffer, 0, offset);downloadLength += offset;fileDownloader.appendDownloadSize(offset);}threadFile.close();inputStream.close();isFinish = true;isStart = false;} catch (IOException e) {e.printStackTrace();downloadLength = -1;}}}public int getDownloadLength() {return downloadLength;}public int getThreadId() {return threadId;}public boolean isFinish() {return isFinish;}public boolean isStart() {return isStart;}
}
下载器:
import android.content.Context;
import android.util.SparseIntArray;import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** Created by lion on 2017/2/7.*/public class FileDownloader {public static final String TAG = "FileDownloader";/*** http connection timeout*/private static int CONNECTION_TIME_OUT = 10 * 1000;private DownloadProgressManager downloadProgressManager;private DownloadRunnable[] downloadThreads;private String tagName = "";private String downloadUrl;private String fileSavePath;private String filename;private int threadNum = 1;private int fileSize = 0;private int currentDownloadSize = 0;private SparseIntArray currentDownloads;public DownloadRunnable[] getDownloadThreads() {return downloadThreads;}public DownloadProgressManager getDownloadProgressManager() {return downloadProgressManager;}public int getFileSize() {return fileSize;}public String getFileSavePath() {return fileSavePath;}public String getFilename() {return filename;}public String getDownloadUrl() {return downloadUrl;}public int getCurrentDownloadSize() {return currentDownloadSize;}public int getThreadNum() {return threadNum;}synchronized int appendDownloadSize(int size) {currentDownloadSize += size;return currentDownloadSize;}public FileDownloader(Context context) {this.currentDownloads = new SparseIntArray();this.downloadProgressManager = new DownloadProgressManager(context);}private void requestFileInfo(String downloadUrl) throws RuntimeException {try {HttpURLConnection connection = (HttpURLConnection)new URL(downloadUrl).openConnection();connection.setConnectTimeout(CONNECTION_TIME_OUT);connection.setRequestMethod("GET");//set accept file meta-data typeconnection.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg," +" image/pjpeg, application/x-shockwave-flash, application/xaml+xml, " +"application/vnd.ms-xpsdocument, application/x-ms-xbap, " +"application/x-ms-application, application/vnd.ms-excel, " +"application/vnd.ms-powerpoint, application/msword, */*");connection.setRequestProperty("Accept-Language", "zh-CN");connection.setRequestProperty("Referer", downloadUrl);connection.setRequestProperty("Charset", "UTF-8");connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; " +"Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; " +".NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
// connection.setRequestProperty("Connection", "Keep-Alive");connection.connect();if (connection.getResponseCode() == 200) {fileSize = connection.getContentLength();if (fileSize <= 0) {throw new RuntimeException(TAG + " Unknown file size");}filename = getFilename(connection);} else {throw new RuntimeException(TAG + " Server Response Code is "+ connection.getResponseCode());}} catch (IOException e) {e.printStackTrace();}}private String getFilename(HttpURLConnection connection) {String filename = downloadUrl != null ?downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1) : null;if (filename == null || "".equals(filename.trim())) {//如果获取不到文件名称for (int i = 0; ; i++) {String mine = connection.getHeaderField(i);if (mine == null) break;if ("content-disposition".equals(connection.getHeaderFieldKey(i).toLowerCase())) {Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase());if (m.find()) return m.group(1);}}filename = UUID.randomUUID() + ".tmp";//默认取一个文件名}return filename;}public void prepare(String downloadUrl, String fileSavePath, int threadNum) {this.downloadUrl = downloadUrl;this.fileSavePath = fileSavePath;requestFileInfo(downloadUrl);SparseIntArray progresses = downloadProgressManager.getProgress(downloadUrl);if (threadNum <= 0) {threadNum = this.threadNum;} else {this.threadNum = threadNum;}if (progresses != null && progresses.size() > 0) {threadNum = progresses.size();for (int i = 0; i < progresses.size(); i++) {currentDownloadSize += progresses.get(i);}}int block = fileSize % threadNum == 0 ?fileSize / threadNum : fileSize / threadNum + 1;downloadThreads = new DownloadRunnable[threadNum];for (int i = 0; i < threadNum; i++) {downloadThreads[i] = new DownloadRunnable(this, i, block,progresses != null && progresses.size() == threadNum ?progresses.valueAt(progresses.keyAt(i)) == -1 ? 0 :progresses.valueAt(progresses.keyAt(i)) : 0);}}public void start(OnDownloadListener listener) {boolean isFinish = false;int lastDownloadSize = 0;int speed = 0;Date current = new Date();while (!isFinish) {if (listener != null) {int percent = (int) (currentDownloadSize / (float) fileSize * 100);long time = new Date().getTime() - current.getTime();speed = (int) ((currentDownloadSize - lastDownloadSize) / 1024f / time * 1000f);listener.onUpdate(fileSize, currentDownloadSize, speed, percent);if (percent == 100) {downloadProgressManager.finishDownload(downloadUrl);break;}}current = new Date();lastDownloadSize = currentDownloadSize;updateProgress();isFinish = checkFinish();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}// System.out.println(tagName + " DOWNLOAD FINISH");if (listener != null) {listener.onUpdate(fileSize, fileSize, 0, 100);}}private boolean checkFinish() {if (downloadThreads != null && downloadThreads.length > 0) {for (DownloadRunnable downloadThread : downloadThreads) {if (!downloadThread.isFinish()) {System.out.println("checkFinish false");return false;}}return true;}System.out.println("checkFinish true");return false;}public boolean isFinish() {return checkFinish();}void updateProgress() {for (DownloadRunnable downloadThread : downloadThreads) {updateProgress(downloadThread.getThreadId(), downloadThread.getDownloadLength());}}synchronized void updateProgress(int threadId, int downloaded) {currentDownloads.put(threadId, downloaded);downloadProgressManager.saveProgress(downloadUrl, currentDownloads);
// SparseIntArray progress = downloadProgressManager.getProgress(downloadUrl);
// for (int i = 0; i < progress.size(); i++) {// System.out.println("prepare progress = " + progress.valueAt(progress.keyAt(i)));
// }}public boolean isStart() {for (DownloadRunnable runnable : downloadThreads) {if (runnable.isStart()) {return true;}}return false;}static int getConnectionTimeOut() {return CONNECTION_TIME_OUT;}static void setConnectionTimeOut(int timeOut) {CONNECTION_TIME_OUT = timeOut;}public interface OnDownloadListener {void onUpdate(int totalSize, int currentSize, int speed, int percent);}public void setTagName(String tagName) {this.tagName = tagName;}
}
下载管理器:
import android.content.Context;import java.util.ArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;/*** Created by lion on 2017/2/8.*/public class DownloadManager {private static int PARALLEL_DOWNLOAD_SIZE = 6;private static DownloadManager instance;private Context context;private Executor downloadExecutor;private ArrayList<FileDownloader> fileDownloaders;public static DownloadManager getInstance(Context context) {if (instance == null) {instance = new DownloadManager(context);}return instance;}public DownloadManager(Context context) {this.context = context;downloadExecutor = Executors.newFixedThreadPool(PARALLEL_DOWNLOAD_SIZE);
// downloadExecutor = Executors.newCachedThreadPool();fileDownloaders = new ArrayList<>();}public void download(String name, final String downloadUrl, final String fileSavePath, final int threadNum,final FileDownloader.OnDownloadListener listener) {for (FileDownloader downloader : fileDownloaders) {if (downloader.isFinish()) {downloader.setTagName(name);startDownload(downloader, downloadUrl, fileSavePath, threadNum, listener);return;}}FileDownloader currentDownloader = new FileDownloader(context);currentDownloader.setTagName(name);fileDownloaders.add(currentDownloader);startDownload(currentDownloader, downloadUrl, fileSavePath, threadNum, listener);}public void download(final String downloadUrl, final String fileSavePath, final int threadNum,final FileDownloader.OnDownloadListener listener) {for (FileDownloader downloader : fileDownloaders) {if (downloader.isFinish()) {startDownload(downloader, downloadUrl, fileSavePath, threadNum, listener);return;}}FileDownloader currentDownloader = new FileDownloader(context);fileDownloaders.add(currentDownloader);startDownload(currentDownloader, downloadUrl, fileSavePath, threadNum, listener);}private synchronized void startDownload(final FileDownloader currentDownloader,final String downloadUrl, final String fileSavePath,final int threadNum,final FileDownloader.OnDownloadListener listener) {downloadExecutor.execute(new Runnable() {@Overridepublic void run() {currentDownloader.prepare(downloadUrl, fileSavePath,threadNum);if (currentDownloader.getDownloadThreads() != null) {for (DownloadRunnable runnable :currentDownloader.getDownloadThreads()) {downloadExecutor.execute(runnable);}}currentDownloader.start(listener);}});}public static void setConnectionTimeOut(int timeOut) {FileDownloader.setConnectionTimeOut(timeOut);}public static void setParallelDownloadSize(int size) {PARALLEL_DOWNLOAD_SIZE = size;}
}
android 多线程下载,断点续传,线程池相关推荐
- Android多线程下载断点续传
先上图看卡结果: GITHUB:Android多线程下载断点续传 下载杵这儿 如图所示点击下载就开始下载,点击停止就会停止再次点击下载就会接着下载了. 设计思路是这样的: 首先通过广播将下载信息传递给 ...
- Android AsyncTask两种线程池分析和总结
转自:http://bbs.51cto.com/thread-1114378-1-1.html Android AsyncTask两种线程池分析和总结 (一) 前言 在android Async ...
- 更好的Android多线程下载框架
/*** 作者:Pich* 原文链接:http://me.woblog.cn/* QQ群:129961195* Github:https://github.com/lifengsofts*/ 概述 为 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
转载自 Java多线程系列--"JUC线程池"06之 Callable和Future Callable 和 Future 简介 Callable 和 Future 是比较有趣的一 ...
- linux下c语言线程传参数,【linux】C语言多线程中运行线程池,在线程池中运行线程池,,传递的结构体参数值为空/NULL/0...
C语言多线程中运行线程池,在线程池中运行线程池,,传递的结构体参数值为空/NULL/0 本贴问题,之前已经提问过一次,当时已经解决了,原贴在这里https://segmentfault.com/q/1 ...
- android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)...
一,原生的DownloadManager 从Android 2.3(API level 9)开始,Android以Service的方式提供了全局的DownloadManager来系统级地优化处理长时间 ...
- Android 多线程下载以及断点续传
多线程下载 在日常开发中,我们不可避免的会接到类似这样的需求,下载一个比较大的素材文件或者安装包文件,以此实现APP的自动更新,APP内的素材替换等.由于一般此类文件都比较大,一般会在50M以上,如果 ...
- android多线程下载程序卡死,android 多线程下载与断点续传
多线程下载: 下载速度更快,服务器对每个线程平分资源,故线程越多,得到的资源越多,下载速度越快. 断点续传: 下载中断,再次下载时从上一次下载结束的位置开始下载,防止重复下载 下载结束后 代码: pa ...
- android多线程下载3
今天跟大家一起分享下android开发中比较难的一个环节,可能很多人看到这个标题就会感觉头很大,的确如果没有良好的编码能力和逻辑思维,这块是很难搞明白的,前面2次总结中已经为大家分享过有关技术的一些基 ...
最新文章
- #时间预测算法_改进的智慧交通系统出行时间预测算法
- 进阶必备:CNN经典论文代码复现 | 附下载链接
- 关于vs编码格式UTF8中文处理方式
- 2的32次方减1用python,第二章python入门
- 一键生成Vue.js + Web API前后端集成项目
- P3811-[模板]乘法逆元【线性求逆元】
- 计算机图形图像发展前景,计算机图形学发展前景有什么趋势?
- 未能启用约束。一行或多行中包含违反非空、唯一或外键约束的值。
- UVA11038 How Many O's?题解
- 【[Offer收割]编程练习赛10 C】区间价值
- 分子动力学模拟算法框架
- java实现微博热搜榜_微博热搜数据
- 计算机专业论文选题的8个方法
- Matlab取整函数之Floor\fix\round\ceil取余函数rem\mod
- 用Js怒刷LeetCode
- Quartus-II入门实战
- PLC可编程控制器、单片机开发应用及变频调速综合实训装置
- 微信网页开发(5)--invalid signature/permission denied/offline verifying等错误处理
- 目前的云计算,主要存在的问题有哪些?
- 计算机社团打字比赛规则,金手指打字比赛策划(最终版)