首页 > 代码库 > Android实现多线程断点续传
Android实现多线程断点续传
前言: 项目都快交付阶段了,客户说要改个需求,添加一个断点续传功能。在版本更新,杂志下载或者视频下载的时候实现断点续传。由于时间紧迫,想起了之前研究过一个demo代码,就直接修改使用了,根据自己的方式实现,但是核心代码没变。以后或许会用到,于是就专门写了个demo。
先看一下项目目录结构:
- db--->操作数据库的(创建数据库表,数据的增删改查。)
- util--->工具类
- download--->实现下载(下载器以及自定义线程。)
这里以易信客户端的下载为例,简要介绍。
String downloadPath = "http://gdown.baidu.com/data/wisegame/653346a13ab69081/yixin_146.apk"; String fileName = "易信.apk";
在这里我添加了通知栏,查看进度。
大致思路:根据需要下载文件的总大小,可以自定义几个线程同时下载动作(在这里我给下载器构造方法重载了,默认不设定线程数量的话默认是3个)。
内部使用map键值对的方式缓存线程下载数据,key是线程id,value:是下载量。同时还有个线程数组,这个线程数组和前面的Map是对应的。
在下载器中,有个循环会不断检查每个线程的下载任务是否完成,完成了则将其T掉,这样到最后就都完成后,退出循环,整个下载任务就完成。删除数据库表中的下载记录。
然后每个线程分配下载文件大小值,执行下载。下载过程中,通过回调接口,回调到MainActivity画面中,利用handler实现更新UI。
......
这里具体的问题细节,啊还有很多,大致思路就是这样了。
代码里面我都写了详细的注释,贴点代码把。希望你也能看懂。
package com.example.download; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import com.example.util.Common; import android.util.Log; /** * 用于下载文件的线程。 该线程有被分配的工作量:block * */ public class DownloadThread extends Thread { private static final String TAG = "DownloadThread"; /** * 目标文件 */ private File saveFile; /** * 文件下载路径 */ private URL downUrl; /** * 当前线程需要下载的文件大小 */ private int block; /** * 线程身份识别id */ private int threadId = -1; /** * 之前已经下载的位置 */ private int downLength; /** * 判断当前线程是否结束 */ private boolean isFinished = false; /** * 文件下载器 */ private FileDownloader downloader; /** * @param downloader * 下载器 * @param downUrl * 下载的网络路径 * @param saveFile * 保存的目标文件 * @param block * 下载任务量 * @param downLength * 已经下载的大小 * @param threadId * 线程id */ public DownloadThread(FileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) { this.downUrl = downUrl; this.saveFile = saveFile; this.block = block; this.downloader = downloader; this.threadId = threadId; this.downLength = downLength; } @Override public void run() { if (downLength < block) {// 未下载完成 try { // 使用Get方式下载 HttpURLConnection http = Common.getHttpParams(downUrl); // 开始下载位置 int startPosition = block * (threadId - 1) + downLength; // 结束下载位置 int endPosition = block * threadId - 1; // 设置获取实体数据的范围 http.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition); // 获取文件输入流 InputStream inStream = http.getInputStream(); byte[] buffer = new byte[1024]; int offset = 0; print("Thread " + this.threadId + " start download from position " + endPosition); RandomAccessFile threadfile = new RandomAccessFile( this.saveFile, "rwd"); threadfile.seek(endPosition); while ((offset = inStream.read(buffer, 0, 1024)) != -1) { threadfile.write(buffer, 0, offset); // 下载量累加 downLength += offset; // 更新下载数据库 downloader.update(this.threadId, downLength); downloader.append(offset); } threadfile.close(); inStream.close(); print("Thread " + this.threadId + " download finish"); // 设置下载完成标志位。 this.isFinished = true; } catch (Exception e) { this.downLength = -1; print("Thread " + this.threadId + ":" + e); } } } /** * 打印日志信息 * * @param msg */ private static void print(String msg) { Log.i(TAG, msg); } /** * 下载是否完成 * * @return */ public boolean isFinish() { return isFinished; } /** * 已经下载的内容大小 * * @return 如果返回值为-1,代表下载失败 */ public long getDownLength() { return downLength; } }
下载器:
package com.example.download; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.util.Log; import com.example.db.DBTools; import com.example.util.Common; /** * * 文件下载 * * * */ public class FileDownloader { private static final String TAG = "FileDownloader"; private Context context; /** * 操作数据库的类 */ private DBTools dbTools; /** * 已下载文件长度 */ private int downloadSize = 0; /** * 原始文件长度 */ private int fileSize = 0; /** * 线程数 */ private DownloadThread[] threadArr; /** * 本地保存文件 */ private File saveFile; /** * 默认下载线程数量 */ private static final int defaultThreadSize = 3; /** * 缺省文件下载存放位置 */ private static String defaultDestPath = ""; /** * 用于缓存各个线程下载的长度 * <p> * Key:对应线程id;Value:对应线程已经下载的长度 * </p> */ private Map<Integer, Integer> cacheDownMap = new ConcurrentHashMap<Integer, Integer>(); /** * 每条线程应该下载的长度 */ private int block; /** * 文件下载路径 */ private String downloadUrl; /** * 获取线程数 */ public int getThreadSize() { return threadArr.length; } /** * 获取文件大小 * * @return 需要下载文件的总长度 */ public int getFileSize() { return fileSize; } /** * 累加文件已下载的大小 * * @param size * 此刻下载量 */ protected synchronized void append(int size) { downloadSize += size; } /** * 更新指定线程最后下载的位置 * * @param threadId * 线程id * @param pos * 最后下载的位置 */ protected synchronized void update(int threadId, int pos) { this.cacheDownMap.put(threadId, pos); this.dbTools.update(this.downloadUrl, this.cacheDownMap); } /** * 构建文件下载器 * * @param downloadUrl * 下载路径 * @param fileSaveDir * 文件保存目录 * @param threadNum * 下载线程数 */ public FileDownloader(Context context, String downloadUrl, String destPath, int threadNum) { this.context = context; this.downloadUrl = downloadUrl; dbTools = new DBTools(this.context); checkPath(destPath); this.threadArr = new DownloadThread[threadNum]; setRequest(downloadUrl, destPath); } /** * 检查下载路径是否存在,不存在则创建。。 * @param destPath * @return 是否存在该目录 */ public boolean checkPath(String destPath) { File file = new File(destPath); if (!file.exists()) { boolean bol = file.mkdirs(); return bol; } return true; } /** * @param context * @param downloadUrl */ public FileDownloader(Context context, String downloadUrl) { this.context = context; this.downloadUrl = downloadUrl; this.threadArr = new DownloadThread[defaultThreadSize]; this.dbTools = new DBTools(this.context); dbTools = new DBTools(this.context); checkPath(defaultDestPath); this.threadArr = new DownloadThread[defaultThreadSize]; setRequest(downloadUrl, defaultDestPath); } /** * 封装请求 */ public void setRequest(String downUrl, String destPath) { URL url = null; try { url = new URL(downUrl); HttpURLConnection conn = Common.getHttpParams(url); conn.connect(); // 打印头部信息 printResponseHeader(conn); // 200==》成功连接... if (conn.getResponseCode() == 200) { // 获取文件总大小 this.fileSize = conn.getContentLength(); if (this.fileSize <= 0) throw new RuntimeException("该文件很可能不存在..."); // 获取当前需要下载文件名称 String filename = getFileName(conn); // 目标保存文件 this.saveFile = new File(destPath, filename); // 获取当前路径下载记录包括已经下载数量。 Map<Integer, Integer> logdata = http://www.mamicode.com/dbTools.getData(downUrl);>package com.example.util; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; /** * <p> * </p> * * @author xiangxm */ public class Common { /** * 格式化文件大 * * @param volume * 文件大小 * @return 格式化的字符 */ public static String getVolume(long volume) { float num = 1.0F; String str = null; if (volume < 1024) { str = volume + "B"; } else if (volume < 1048576) { num = num * volume / 1024; str = String.format("%.1f", num) + "K"; } else if (volume < 1073741824) { num = num * volume / 1048576; str = String.format("%.1f", num) + "M"; } else if (volume < 1099511627776L) { num = num * volume / 1073741824; str = String.format("%.1f", num) + "G"; } return str; } /** * * 设置网络参数 */ public static HttpURLConnection getHttpParams(URL httpUrl) { HttpURLConnection http; try { http = (HttpURLConnection) httpUrl.openConnection(); http.setConnectTimeout(5 * 1000); http.setRequestMethod("GET"); http.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, */*"); http.setRequestProperty("Accept-Language", "zh-CN"); http.setRequestProperty("Referer", httpUrl.toString()); http.setRequestProperty("Charset", "UTF-8"); http.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)"); http.setRequestProperty("Connection", "Keep-Alive"); return http; } catch (IOException e) { e.printStackTrace(); } return null; } }源代码下载地址:http://download.csdn.net/detail/xxm282828/7701071
看一下效果图:
下一篇打算: 使用downloadManager实现下载,断点续传。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。