首先声明一点: 这里的多线程下载 并不是指的 多个线程下载一个 文件,而是 每个线程 负责一个文件。真正的多线程 希望后面能给大家带来。

------------- 欢迎 爱学习的小伙伴 加群 -------------

-------------android交流群:230274309-------------

-------------一起分享,一起进步! 需要你们--------------

-------------- 期待各位爱学习的小伙伴们 的到来--------------

本文是接着前面http://blog.csdn.net/u011733020/article/details/47016715继续写的。感兴趣的小伙伴 可以看前面的。

界面效果

2015/7/31改进


2015/7/31 日先说 这次 改进了两点。

第一点 ,前面说过 项目 只适合学习,作为商用的话, 效率不高,是因为 当时 点击暂停 ,在点击下载 继续下载时候,如果文件前面下载部分较大,会比较慢, 因为 java 的 inputstream 的 skip(long size) 跳过字节 这个方法 并不能按照你 想要跳过的字节,而是 跳过的 往往是比较小的,所以 要不断遍历,直到 返回满足 条件 ,比较耗时。打个比方, 文件大小 30M ,你下载了20M,你点了暂停 然后继续点下载,就要跳过这20M,但是你用skip 方法 可能每次跳过 4096 字节,这样 要跳过20M 的时间 就会很长。这样应该好理解。

第二点,原来 项目中, 你这一次下载没有完成,下次在下载是 删除掉原来的 从新 下载,这次 改成继续上次的地方 接着下载。

吐槽下,关于下载,我最近一周 一直在看 开源的download, 但是 无奈水平有限,收获甚微,往往是看到最后 脑袋短路。

这次改的方式比较简单,只改动了 项目中 DownloadManager 这个类。在来看下 DownloadManager 这个类 的run 方法,

<span style="font-family:SimHei;font-size:14px;">@Overridepublic void run() {info.setDownloadState(STATE_DOWNLOADING);// 先改变下载状态notifyDownloadStateChanged(info);File file = new File(info.getPath());// 获取下载文件HttpResult httpResult = null;InputStream stream = null;if (info.getCurrentSize() == 0 || !file.exists()|| file.length() != info.getCurrentSize()) {// 如果文件不存在,或者进度为0,或者进度和文件长度不相符,就需要重新下载info.setCurrentSize(0);file.delete();}httpResult = HttpHelper.download(info.getUrl());if (httpResult == null|| (stream = httpResult.getInputStream()) == null) {info.setDownloadState(STATE_ERROR);// 没有下载内容返回,修改为错误状态notifyDownloadStateChanged(info);} else {try {skipBytesFromStream(stream, info.getCurrentSize());} catch (Exception e1) {e1.printStackTrace();}FileOutputStream fos = null;try {fos = new FileOutputStream(file, true);int count = -1;byte[] buffer = new byte[1024];while (((count = stream.read(buffer)) != -1)&& info.getDownloadState() == STATE_DOWNLOADING) {// 每次读取到数据后,都需要判断是否为下载状态,如果不是,下载需要终止,如果是,则刷新进度fos.write(buffer, 0, count);fos.flush();info.setCurrentSize(info.getCurrentSize() + count);notifyDownloadProgressed(info);// 刷新进度}} catch (Exception e) {info.setDownloadState(STATE_ERROR);notifyDownloadStateChanged(info);info.setCurrentSize(0);file.delete();} finally {IOUtils.close(fos);if (httpResult != null) {httpResult.close();}}// 判断进度是否和app总长度相等if (info.getCurrentSize() == info.getAppSize()) {info.setDownloadState(STATE_DOWNLOADED);notifyDownloadStateChanged(info);} else if (info.getDownloadState() == STATE_PAUSED) {// 判断状态notifyDownloadStateChanged(info);} else {info.setDownloadState(STATE_ERROR);notifyDownloadStateChanged(info);info.setCurrentSize(0);// 错误状态需要删除文件file.delete();}}mTaskMap.remove(info.getId());}</span>

从服务器 返回的数据流 stream 最终是在 HttpHelper 这个类中

<span style="font-family:SimHei;font-size:14px;">HttpResponse response = httpClient.execute(requestBase, httpContext);//访问网络</span>
通过 httpclient 去联网请求的 。

我没有试过 httpclient addHeader("Range", "bytes=" + begin + "-" + end); 可不可以进行继续下载。

而是改成了 通过 httpurlconnection 去请求数据

现在 的run() 方法 是这样的。

<span style="font-family:SimHei;font-size:14px;">@Overridepublic void run() {info.setDownloadState(STATE_DOWNLOADING);// 先改变下载状态notifyDownloadStateChanged(info);File file = new File(info.getPath());// 获取下载文件/**********************************************************///try {try {URL url = new URL(info.getUrl());HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(30000);conn.setReadTimeout(30000);if (!file.exists()) {info.setCurrentSize(0);file.delete();} else if (file.length() > info.getAppSize()) {info.setCurrentSize(0);file.delete();} else if (file.length() == info.getAppSize()) {} else if (file.length() < info.getAppSize()) {info.setCurrentSize(file.length());}if (info.getCurrentSize() == 0 || !file.exists() || file.length() != info.getCurrentSize()) {// 如果文件不存在,或者进度为0,或者进度和文件长度不相符,就需要重新下载info.setCurrentSize(0);file.delete();} else if (file.length() == info.getCurrentSize() && file.length() < info.getAppSize()) {conn.setRequestProperty("Range", "bytes=" + info.getCurrentSize() + "-" + info.getAppSize());}int code = conn.getResponseCode();RandomAccessFile raf = new RandomAccessFile(file, "rw");InputStream is = conn.getInputStream();byte[] buffer = new byte[1024 * 8];int len = -1;int total = 0;// 当前线程下载的总的数据的长度if (code == 200) {} else if (code == 206) {raf.seek(file.length());}while (((len = is.read(buffer)) != -1) && (info.getDownloadState() == STATE_DOWNLOADING)) { // 下载数据的过程。raf.write(buffer, 0, len);total += len;// 需要记录当前的数据。info.setCurrentSize(info.getCurrentSize() + len);notifyDownloadProgressed(info);// 刷新进度}is.close();raf.close();} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ProtocolException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}/*************************对于各种情况,需要删除下载任务,从新下载的 请自己改动代码*****************************/// 判断进度是否和app总长度相等//} catch (Exception e) {//System.out.println(e.toString());//info.setDownloadState(STATE_ERROR);//info.setCurrentSize(0);//file.delete();//e.printStackTrace();//}if (info.getCurrentSize() == info.getAppSize()) {info.setDownloadState(STATE_DOWNLOADED);notifyDownloadStateChanged(info);} else if (info.getDownloadState() == STATE_PAUSED) {// 判断状态notifyDownloadStateChanged(info);} else {info.setDownloadState(STATE_ERROR);notifyDownloadStateChanged(info);info.setCurrentSize(0);// 错误状态需要删除文件file.delete();}/**********************************************************/mTaskMap.remove(info.getId());}</span>

先判断 文件存不存在,以及大小是否满足条件, 在这里做判断

<span style="font-family:SimHei;font-size:14px;">if (info.getCurrentSize() == 0 || !file.exists() || file.length() != info.getCurrentSize()) {// 如果文件不存在,或者进度为0,或者进度和文件长度不相符,就需要重新下载info.setCurrentSize(0);file.delete();} else if (file.length() == info.getCurrentSize() && file.length() < info.getAppSize()) {     conn.setRequestProperty("Range", "bytes=" + info.getCurrentSize() + "-" + info.getAppSize());  }</span>
如果 文件当前大小为0,或者文件不存在,或者长度不等于当前长度,则重新下载,否则 设置 Range

下面 判断 code 正常情况下code =200 表示成功,如果 设置了Range 那么 code 返回 206 表示正常。这个时候 我们通过RandomAccessFile

RandomAccessFile 这个 类实现了RandomAccessFile implements DataInput, DataOutput, 就是一个既可以读 也可以写的类。

RandomAccessFile 这个类来 处理 跳过多少字节, 前面 我说过 inpuStream.skeep() 方法 不准确,但是 RandomAccessFile 这个类是可以的。

<span style="font-family:SimHei;font-size:14px;">RandomAccessFile raf = new RandomAccessFile(file, "rw");InputStream is = conn.getInputStream();byte[] buffer = new byte[1024 * 8];int len = -1;int total = 0;// 当前线程下载的总的数据的长度if (code == 200) {} else if (code == 206) {raf.seek(file.length());}</span>

通过 seek 方法 跳过 这些字节。

然后

<span style="font-family:SimHei;font-size:14px;">while (((len = is.read(buffer)) != -1) && (info.getDownloadState() == STATE_DOWNLOADING)) { // 下载数据的过程。raf.write(buffer, 0, len);total += len;// 需要记录当前的数据。info.setCurrentSize(info.getCurrentSize() + len);notifyDownloadProgressed(info);// 刷新进度}is.close();raf.close();</span>
很普通的代码,把数据写出去。不断刷新当前进度, 最后关闭流。

这样就可以保证 快速的 暂停 继续下载, 并且 本次下载 没有完成,点了暂停, 下次进应用,继续下载的时候 会接着上一次下载,但是断网,或者你自己把网关掉 ,下次在恢复网络,或者 在点下载,我并没有处理,有需要的就自己处理下吧,应该是捕获异常 seckouttimeException,然后保存数据。自己动手试下就知道了。

本次就到这里。 关于多个线程 分段下载一个文件, 等我有时间 在整理。

共勉

更多相关文章

  1. cocos2d-x 3.0rc2中读取sqlite文件
  2. android通过http上传文件(图片)
  3. 如何下载并编译Android内核源码goldfish(图文)
  4. Android(安卓)7.1 双卡双待机器,首选网络类型设置 详细分析
  5. Android(安卓)Dialog详解
  6. java中采用Pull解析器对XML文件进行解析
  7. eclipse关联android源码
  8. Android系统文件目录结构
  9. android addr2line使用

随机推荐

  1. Android(安卓)Studio单元测试
  2. android AIDL服务
  3. Android入门系列(一)--Android的目录结构
  4. Android 模拟HTTP协议的编码问题 Android
  5. Android Studio使用教程、工程目录结构、
  6. Eclipse SDK 3.7.2无法安装android SDK
  7. Android与H5相机、相册笔记
  8. Android(安卓)NDK学习笔记3-入门案例篇
  9. 【Android学习之旅】1、Android入门介绍
  10. Android(安卓)Animation