Android 中实现多线程下载和断点续传的原理和代码
2016-11-16 15:18
519 查看
在上一章,用Java代码实现了 多线程下载和断点续传, 现在我们将它移植到 Android 上面.
介绍一下这个类的使用方法.
快速下载
1. 该类的构造方法需要提供三个参数.
2.
- 参数一 : 要下载的资源网络路径.
- 参数二 : 资源保存在手机上的路径
- 参数三 : 开启的线程个数.默认为 3.
3. 创建类的实例
4. 调用类的 download() 方法.
5. 文件会被下载到指定的目录.
下载的过程中更新进度条
如果在下载的时候需要在页面展示进度条.
为每一个线程创建一个进度条控件,封装到
获取线程的下载结果
1. 将handler对象设置给MuchThreadDown 的对象.
2. Message中封装的数据如下
3. `Message msg = Message.obtain();
下面这个类能帮助我们实现多线程下载和断点续传功能.
介绍一下这个类的使用方法.
快速下载
1. 该类的构造方法需要提供三个参数.
2.
public MuchThreadDown(String path, String targetFilePath, int threadCount)
- 参数一 : 要下载的资源网络路径.
- 参数二 : 资源保存在手机上的路径
- 参数三 : 开启的线程个数.默认为 3.
3. 创建类的实例
MuchThreadDown threadDown = new MuchThreadDown(url_str, path, progress);
4. 调用类的 download() 方法.
5. 文件会被下载到指定的目录.
下载的过程中更新进度条
如果在下载的时候需要在页面展示进度条.
为每一个线程创建一个进度条控件,封装到
Map<Integer, ProgressBar>map中的key代表线程的id,比如有当前开了三个线程, 线程的id就是0, 1, 2 进度条就需要我们动态的去创建了,将创建的进度条封装到map中.
threadDown.setProgressBar_map(progressBar_map);设置给 MuchThreadDown 的对象.
获取线程的下载结果
1. 将handler对象设置给MuchThreadDown 的对象.
threadDown.setHandler(handler);当一个线程下载完成的时候,返回 1 .
2. Message中封装的数据如下
3. `Message msg = Message.obtain();
msg.what = 66; msg.obj = 1; handler.sendMessage(msg);`
package com.yb.muchthreaddown.util; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.Map; import android.os.Handler; import android.os.Message; import android.widget.ProgressBar; /** * 多线程下载 和 断点续传 for Android * @author 杨斌. * */ public class MuchThreadDown { // private String path = "http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3"; //下载路径 private String path = "http://117.169.69.238/mp3.9ku.com/m4a/186947.m4a"; private String targetFilePath="/"; //下载文件存放目录 private int threadCount = 3; //线程数量 public Map<Integer, ProgressBar> progressBar_map;//键 线程id, 值 进度条控件 private Handler handler; /** * 构造方法 * @param path 要下载文件的网络路径 * @param targetFilePath 保存下载文件的目录 * @param threadCount 开启的线程数量,默认为 3 */ public MuchThreadDown(String path, String targetFilePath, int threadCount) { this.path = path; this.targetFilePath = targetFilePath; this.threadCount = threadCount; } public Map<Integer, ProgressBar> getProgressBar_map() { return progressBar_map; } public Handler getHandler() { return handler; } public void setHandler(Handler handler) { this.handler = handler; } public void setProgressBar_map(Map<Integer, ProgressBar> progressBar_map) { this.progressBar_map = progressBar_map; } /** * 下载文件 */ public void download() throws Exception{ //连接资源 URL url = new URL(path); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(10000); int code = connection.getResponseCode(); if(code == 200){ //获取资源大小 int connectionLength = connection.getContentLength(); System.out.println(connectionLength); //在本地创建一个与资源同样大小的文件来占位 RandomAccessFile randomAccessFile = new RandomAccessFile(new File(targetFilePath,getFileName(url)), "rw"); randomAccessFile.setLength(connectionLength); /* * 将下载任务分配给每个线程 */ int blockSize = connectionLength/threadCount;//计算每个线程理论上下载的数量. for(int threadId = 0; threadId < threadCount; threadId++){//为每个线程分配任务 int startIndex = threadId * blockSize; //线程开始下载的位置 int endIndex = (threadId+1) * blockSize -1; //线程结束下载的位置 if(threadId == (threadCount - 1)){ //如果是最后一个线程,将剩下的文件全部交给这个线程完成 endIndex = connectionLength - 1; } if(progressBar_map != null){ new DownloadThread(threadId, startIndex, endIndex, progressBar_map.get(threadId),handler).start();//开启线程下载 }else{ new DownloadThread(threadId, startIndex, endIndex, null,handler).start();//开启线程下载 } } // randomAccessFile.close(); } } //下载的线程 private class DownloadThread extends Thread{ private int threadId; private int startIndex; private int endIndex; private ProgressBar progressBar; private int currentThreadTotal;//当前线程下载文件的总大小 private Handler handler; public DownloadThread(int threadId, int startIndex, int endIndex, ProgressBar progressBar,Handler handler) { this.threadId = threadId; this.startIndex = startIndex; this.endIndex = endIndex; this.currentThreadTotal = endIndex - startIndex + 1; this.progressBar = progressBar; this.handler = handler; } @Override public void run() { System.out.println("线程"+ threadId + "开始下载"); try { //分段请求网络连接,分段将文件保存到本地. URL url = new URL(path); //加载下载位置的文件 File downThreadFile = new File(targetFilePath, getFileName(url) +"_downThread_" + threadId+".dt"); RandomAccessFile downThreadStream = null; if(downThreadFile.exists()){//如果文件存在 downThreadStream = new RandomAccessFile(downThreadFile,"rwd"); String startIndex_str = downThreadStream.readLine(); this.startIndex = Integer.parseInt(startIndex_str);//设置下载起点 }else{ downThreadStream = new RandomAccessFile(downThreadFile,"rwd"); } HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(10000); //设置分段下载的头信息。 Range:做分段数据请求用的。格式: Range bytes=0-1024 或者 bytes:0-1024 connection.setRequestProperty("Range", "bytes="+ startIndex + "-" + endIndex); System.out.println("线程_"+threadId + "的下载起点是 " + startIndex + " 下载终点是: " + endIndex); if(connection.getResponseCode() == 206){//200:请求全部资源成功, 206代表部分资源请求成功 InputStream inputStream = connection.getInputStream();//获取流 RandomAccessFile randomAccessFile = new RandomAccessFile( new File(targetFilePath,getFileName(url)), "rw");//获取前面已创建的文件. randomAccessFile.seek(startIndex);//文件写入的开始位置. /* * 将网络流中的文件写入本地 */ byte[] buffer = new byte[1024*100]; int length = -1; int total = 0;//记录本次下载文件的大小 boolean flag = false; if(progressBar != null){ flag = true; progressBar.setMax(currentThreadTotal); } while((length = inputStream.read(buffer)) > 0){ randomAccessFile.write(buffer, 0, length); total += length; /* * 将当前现在到的位置保存到文件中 */ int currentThreadPosition = startIndex + total; //当前文件下载位置. downThreadStream.seek(0); downThreadStream.write((currentThreadPosition + "").getBytes("UTF-8")); //设置进度条数据. if(flag){ progressBar.setProgress(currentThreadTotal - (endIndex - currentThreadPosition)); } } if(handler != null){ Message msg = Message.obtain(); msg.what = 66; msg.obj = 1; handler.sendMessage(msg); } downThreadStream.close(); inputStream.close(); randomAccessFile.close(); cleanTemp(downThreadFile);//删除临时文件 System.out.println("线程"+ threadId + "下载完毕"); }else{ System.out.println("响应码是" +connection.getResponseCode() + ". 服务器不支持多线程下载"); } } catch (Exception e) { e.printStackTrace(); } } } //删除线程产生的临时文件 private synchronized void cleanTemp(File file){ file.delete(); } //获取下载文件的名称 private String getFileName(URL url){ String filename = url.getFile(); return filename.substring(filename.lastIndexOf("/")+1); } public static void main(String[] args) { try { new MuchThreadDown(null, null, 3).download(); } catch (Exception e) { e.printStackTrace(); } } }
相关文章推荐
- 在android中利用多线程实现对控件的更新(动态修改文本框中的值)。简述原理并上传代码。 .
- Android中悬浮窗口的实现原理和示例代码
- java和Android文件下载断点续传和图片下载代码实现,可直接复制简单实现
- 判断手机是否ROOT 代码实现+ Android 操作系统 获取Root权限 原理解析
- Android中实现多线程下载的两种方式示例及浅析之二(Thread加断点续传)
- Android中悬浮窗口的实现原理和示例代码
- Android之 多线程下载、断点续传 实现
- android底部菜单栏实现原理与代码
- android中的binder机制(不是说代码,说说实现的原理,你不得错过)
- Android中悬浮窗口的实现原理和示例代码
- Android中悬浮窗口的实现原理和示例代码
- Android中悬浮窗口的实现原理和示例代码
- android Setting中隐藏项实现原理与代码
- android 开发之蜂鸣提示音和震动提示的实现原理与参考代码
- Android中悬浮窗口的实现原理和示例代码
- Android中悬浮窗口的实现原理和示例代码
- Android中悬浮窗口的实现原理和示例代码
- Android之 多线程下载、断点续传 实现
- Android-使用xUtils实现简单断点续传(主要代码)
- Android中实现多线程下载的两种方式示例及浅析之一(无断点续传)