Android多线程下载断点续传
先上图看卡结果:
GITHUB:Android多线程下载断点续传
下载杵这儿
如图所示点击下载就开始下载,点击停止就会停止再次点击下载就会接着下载了。
设计思路是这样的:
首先通过广播将下载信息传递给DownService,DownService根据文件URL获取文件大小,再通过DownTask将下载任务分配,并且通过广播当点击停止下载时将下载进度保存在数据库中,当点击开始下载时再从数据库中获取到保存的进度,继续下载。
代码结构:
核心类是 DownLoadService,java 和DownTask.java将这两个类贴出来:
package com.example.downloaddemo.services;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.http.HttpStatus;
import com.example.downloaddemo.enties.FileInfo;
import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;public class DownLoadService extends Service {// 设置存储路劲public static final String DOWN_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/downloads";public static final String ACTION_START = "ACTION_START";public static final String ACTION_STOP = "ACTION_STOP";public static final String ACTION_FINISH = "ACTION_FINISH";public static final String ACTION_UPDATE = "ACTION_UPDATE";public static final int MSG_INIT = 0;// private DownLoadTask mDownLoadTask=null;private Map<Integer, DownLoadTask> mTasks = new LinkedHashMap<Integer, DownLoadTask>();@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 获取Activity传来的数据if (ACTION_START.equals(intent.getAction())) {FileInfo fileinfo = (FileInfo) intent.getSerializableExtra("fileinfo");InitThread minitThread=new InitThread(fileinfo);DownLoadTask.sExecutorService.execute(minitThread);Log.i("test", "start:" + fileinfo.toString());} else if (ACTION_STOP.equals(intent.getAction())) {
FileInfo fileinfo = (FileInfo) intent.getSerializableExtra("fileinfo");// 从集合中获取下载任务DownLoadTask task = mTasks.get(fileinfo.getId());if (task != null) {// 停止下载任务task.isPause = true;}Log.i("test", "stop:" + fileinfo.toString());}return super.onStartCommand(intent, flags, startId);}@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case MSG_INIT:FileInfo info = (FileInfo) msg.obj;Log.i("test", "init:" + info.toString());// 开启下载任务,默认为三个线程下载DownLoadTask task = new DownLoadTask(DownLoadService.this,info, 3);task.downLoad();// 把下载任务添加到集合中mTasks.put(info.getId(), task);break;default:break;}}};/*** * 初始化子线程*/class InitThread extends Thread {private FileInfo mfileInfo = null;public InitThread(FileInfo mfileInfo) {super();this.mfileInfo = mfileInfo;}@Overridepublic void run() {HttpURLConnection conn = null;RandomAccessFile raf = null;try {// 连接网络文件URL url = new URL(mfileInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(3000);conn.setRequestMethod("GET");int length = -1;// 获取文件长度if (conn.getResponseCode() == HttpStatus.SC_OK) {length = conn.getContentLength();}if (length <= 0) {return;}File dir = new File(DOWN_PATH);if (!dir.exists()) {dir.mkdir();}// 在本地文件并设置长度File file = new File(dir,mfileInfo.getFileName());// 特殊的输出流能够 任任意位置写入raf = new RandomAccessFile(file,"rwd");// 设置本地文件的长度raf.setLength(length);mfileInfo.setLength(length);mHandler.obtainMessage(MSG_INIT, mfileInfo).sendToTarget();} catch (Exception e) {// TODO: handle exception} finally {try {// 关闭流操作和网络连接操作raf.close();conn.disconnect();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}super.run();}}}
下面是DownLoadTask.java的代码:
package com.example.downloaddemo.services;
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.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.http.HttpStatus;
import com.example.downloaddemo.db.ThreadDAO;
import com.example.downloaddemo.db.ThreadDAOImpl;
import com.example.downloaddemo.enties.FileInfo;
import com.example.downloaddemo.enties.ThreadInfo;
import android.content.Context;
import android.content.Intent;
/*** * 下载任务* */
public class DownLoadTask {private Context mContext = null;private FileInfo mFileInfo = null;private ThreadDAO mDao = null;private int mfinished = 0;public boolean isPause = false;private int threadCount = 1;// 线程数量private List<DownLoad> mThreadList = null;// 线程集合方便管理分段下载线程
//使用带缓存型池子,先查看池中有没有以前建立的线程,如果有,
//就reuse.如果没有,就建一个新的线程加入池中
//缓存型池子通常用于执行一些生存期很短的异步型任务,
//能reuse的线程,必须是timeout IDLE内的池中线程,
//缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。public static ExecutorService sExecutorService=Executors.newCachedThreadPool();public DownLoadTask(Context mContext, FileInfo mFileInfo, int threadCount) {super();this.mContext = mContext;this.mFileInfo = mFileInfo;mDao = new ThreadDAOImpl(mContext);}public void downLoad() {// 读取数据库的线程信息
List<ThreadInfo> mThreadInfos = mDao.getThreads(mFileInfo.getUrl());if (mThreadInfos.size() == 0) {// 获取每个线程下载的长度int length = mFileInfo.getLength() / threadCount;// 创建线程下载信息for (int i = 0; i < threadCount; i++) {ThreadInfo threadInfo = new ThreadInfo(i, mFileInfo.getUrl(), i* length, (i + 1) * length, 0);if (i == threadCount - 1) {threadInfo.setEnd(mFileInfo.getLength());}// 添加到线程信息集合中mThreadInfos.add(threadInfo);// 向数据库中插入线程信息mDao.insertThread(threadInfo);}}mThreadList = new ArrayList<DownLoadTask.DownLoad>();// 启动多个线程来下载for (ThreadInfo info : mThreadInfos) {DownLoad download = new DownLoad(info);DownLoadTask.sExecutorService.execute(download);mThreadList.add(download);}}/*** 判断下载线程是否都下载完毕*/private synchronized void checkAllThreadsFinished() {boolean allFinished = true;for (DownLoad download : mThreadList) {if (!download.isfinished) {allFinished = false;break;}}if (allFinished) {// 下载完毕删除线程信息mDao.deleteThread(mFileInfo.getUrl());// 发送广播到ActivityIntent intent = new Intent(DownLoadService.ACTION_FINISH);intent.putExtra("fileInfo", mFileInfo);mContext.sendBroadcast(intent);}}class DownLoad extends Thread {private ThreadInfo mThreadInfo = null;public boolean isfinished = false;// 表示线程是否下载完毕public DownLoad(ThreadInfo mThreadInfo) {super();this.mThreadInfo = mThreadInfo;}@Overridepublic void run() {// 打开连接HttpURLConnection conn = null;RandomAccessFile raf = null;InputStream ins = null;try {URL url = new URL(mThreadInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(3000);conn.setRequestMethod("GET");int start = mThreadInfo.getStart() + mThreadInfo.getFinished();// 设置下载位置conn.setRequestProperty("Range","bytes=" + "-" + mThreadInfo.getEnd());// 设置文件写入位置File file = new File(DownLoadService.DOWN_PATH,mFileInfo.getFileName());raf = new RandomAccessFile(file, "rwd");//移动到指定位置raf.seek(start);Intent intent = new Intent(DownLoadService.ACTION_UPDATE);mfinished += mThreadInfo.getFinished();// 开始下载if (conn.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {// 读取数据ins = conn.getInputStream();byte[] buffer = new byte[1024 * 4];int length = -1;long time = System.currentTimeMillis();while ((length = ins.read(buffer)) != -1) {// 写入文件raf.write(buffer, 0, length);// 把下载进度发送广播更新UI// 累加整文件完成进度mfinished += length;// 累加每个线程完成的进度mThreadInfo.setFinished(mThreadInfo.getFinished()+ length);if (System.currentTimeMillis() - time > 1000) {time = System.currentTimeMillis();intent.putExtra("finished", mfinished * 100/ mFileInfo.getLength());intent.putExtra("id", mFileInfo.getId());mContext.sendBroadcast(intent);}// 下载暂停保存进度if (isPause) {mDao.updateThread(mThreadInfo.getUrl(),mThreadInfo.getId(),mThreadInfo.getFinished());return;}
}isfinished = true;// 检查下载任务是否完成checkAllThreadsFinished();}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {conn.disconnect();ins.close();raf.close();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}super.run();}}}
没写之前我在想多线程下载之后如何下载下来的刷数据拼接在一起,后来查看JavaAPI之后使用了用Java的RandomAccessFile操作就可以写入到指定的位,DownLoadService获取长度后,就在存储的位置创建该文件。
HttpURLConnection conn = null;RandomAccessFile raf = null;try {// 连接网络文件URL url = new URL(mfileInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(3000);conn.setRequestMethod("GET");int length = -1;// 获取文件长度if (conn.getResponseCode() == HttpStatus.SC_OK) {length = conn.getContentLength();}if (length <= 0) {return;}File dir = new File(DOWN_PATH);if (!dir.exists()) {dir.mkdir();}// 在本地文件并设置长度File file = new File(dir, mfileInfo.getFileName());// 特殊的输出流能够 任任意位置写入raf = new RandomAccessFile(file, "rwd");// 设置本地文件的长度raf.setLength(length);mfileInfo.setLength(length);mHandler.obtainMessage(MSG_INIT, mfileInfo).sendToTarget();
在写入文件时:不同的线程获取对应的数据写在对应的文件位置就可以了不存在拼接问题。举个栗子:
T1下载0–5;T2下载6—-10;T3下载11—-15,就可以啦下载完写到对应得位置即可。
在写入的时候设置么每写入 byte[1024 * 4],通知UI更新进度条,并进行总的进度累加,下载完后在判断多个线程下载一个文件是不是都下载完成了,如果下载完成了就通知弹出下载完成提示。
核心代码如下:
// 打开连接HttpURLConnection conn = null;RandomAccessFile raf = null;InputStream ins = null;try {URL url = new URL(mThreadInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(3000);conn.setRequestMethod("GET");int start = mThreadInfo.getStart() + mThreadInfo.getFinished();// 设置下载位置conn.setRequestProperty("Range","bytes=" + "-" + mThreadInfo.getEnd());// 设置文件写入位置File file = new File(DownLoadService.DOWN_PATH,mFileInfo.getFileName());raf = new RandomAccessFile(file, "rwd");//移动到指定位置raf.seek(start);Intent intent = new Intent(DownLoadService.ACTION_UPDATE);mfinished += mThreadInfo.getFinished();// 开始下载if (conn.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {// 读取数据ins = conn.getInputStream();byte[] buffer = new byte[1024 * 4];int length = -1;long time = System.currentTimeMillis();while ((length = ins.read(buffer)) != -1) {// 写入文件raf.write(buffer, 0, length);// 把下载进度发送广播更新UI// 累加整文件完成进度mfinished += length;// 累加每个线程完成的进度mThreadInfo.setFinished(mThreadInfo.getFinished()+ length);if (System.currentTimeMillis() - time > 1000) {time = System.currentTimeMillis();intent.putExtra("finished", mfinished * 100/ mFileInfo.getLength());intent.putExtra("id", mFileInfo.getId());mContext.sendBroadcast(intent);}// 下载暂停保存进度if (isPause) {mDao.updateThread(mThreadInfo.getUrl(),mThreadInfo.getId(),mThreadInfo.getFinished());return;}}isfinished = true;// 检查下载任务是否完成checkAllThreadsFinished();
线程通过使用线程池来管理
ExecutorService sExecutorService=Executors.newCachedThreadPool();
//使用带缓存型池子,先查看池中有没有以前建立的线程,如果有,就reuse.如果没有,就建一个新的线程加入池中
//缓存型池子通常用于执行一些生存期很短的异步型任务,能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
GITHUB:
Android多线程下载断点续传相关推荐
- android 多线程下载,断点续传,线程池
android 多线程下载,断点续传,线程池 你可以在这里看到这个demo的源码: https://github.com/onlynight/MultiThreadDownloader 效果图 这张效 ...
- 更好的Android多线程下载框架
/*** 作者:Pich* 原文链接:http://me.woblog.cn/* QQ群:129961195* Github:https://github.com/lifengsofts*/ 概述 为 ...
- android多线程下载原理,安卓多线程断点续传下载功能(靠谱第三方组件,原理demo)...
一,原生的DownloadManager 从Android 2.3(API level 9)开始,Android以Service的方式提供了全局的DownloadManager来系统级地优化处理长时间 ...
- android多线程下载程序卡死,android 多线程下载与断点续传
多线程下载: 下载速度更快,服务器对每个线程平分资源,故线程越多,得到的资源越多,下载速度越快. 断点续传: 下载中断,再次下载时从上一次下载结束的位置开始下载,防止重复下载 下载结束后 代码: pa ...
- Android 多线程下载以及断点续传
多线程下载 在日常开发中,我们不可避免的会接到类似这样的需求,下载一个比较大的素材文件或者安装包文件,以此实现APP的自动更新,APP内的素材替换等.由于一般此类文件都比较大,一般会在50M以上,如果 ...
- android多线程下载3
今天跟大家一起分享下android开发中比较难的一个环节,可能很多人看到这个标题就会感觉头很大,的确如果没有良好的编码能力和逻辑思维,这块是很难搞明白的,前面2次总结中已经为大家分享过有关技术的一些基 ...
- Android -- 多线程下载
因为Android应用程序是java写的,基本上很多java写的程序都可以直接照搬到Android上面,移植性非常Good.这里讲一下多线程下载,就是每个线程都下载自己的那部分,那么就需要平均分配分割 ...
- *Android 多线程下载 仿下载助手(改进版)
首先声明一点: 这里的多线程下载 并不是指的 多个线程下载一个 文件,而是 每个线程 负责一个文件.真正的多线程 希望后面能给大家带来. ------------- 欢迎 爱学习的小伙伴 加群 ...
- Android 多线程下载文件原理霸气解析介绍 -----文件的下载(3)
1.首先我们先创建好下载的位置–根据url创建文件. /*** <p>Title: FlieStorageManager</p >* <p>Description: ...
最新文章
- iOS设计模式 - 生成器
- linux个人常用命令备注
- samba 问题Windows能看到文件夹但是不能打开(路径写错了)
- 【✅windows奇怪的知识增加了✅】windows系统下快速删除海量小文件方法
- Socket详解——全面明了
- ffmpegframegrabber 时间戳不同步_多传感器融合中的时间硬同步1-论文阅读
- Python:通过SNMP协议获取H3C、华为交换机的VLAN信息及ARP地址表
- 【蓝桥杯单片机】实战训练:基于15单片机的距离测试及电压数据采集响应系统(超声波测距、上位机、DAC输出)
- 刚来公司时我却做了一件最傻的事
- 参数嗅探(Parameter Sniffing)(1/2)
- SinoBBD:以后发优势提供不一样的一体化融合CDN服务
- 第一次的爬虫小试(爬取网页文本)
- win安装android系统服务,win7系统安装安卓软件WindowsAndroid的方法
- Cocos Creator 使用摇杆控制角色移动
- 算法设计 L型组件填图问题
- 抖音直播可以看全场回放了?怎么进行有效复盘?
- html 朋友圈视频样式,微信开放视频号卡片形式分享朋友圈权限
- 使用html2canvas生成海报
- 中国市场 Android App 兼容性报告
- 全国计算机网络评课测试,第一次网络评课
热门文章
- 【EMC专题】浪涌抗扰度测试
- android接入阿里百川商务SDK
- iphone 计算机知道密码忘了,苹果笔记本密码忘了怎么办_苹果笔记本密码忘记如何解决-win7之家...
- 教你一招解决百度网盘限速
- 自己实现一个字符串拼接函数
- 基于圣邦微SGM41526多节锂电池充放电电源管理电路分析
- DTS 及其在PG 数据库生态中的应用
- js简单实现拦截访问指定网址
- python与机器人王国_【工业机器人】盘点日本最值得关注的工业机器人13大巨头!...
- java ssm企业车辆汽车信息管理系统