J哥---------Android 多线程下载 仿下载助手(改进版)

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

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

 -------------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 方法,

	@Override
		public 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,或者进度和文件长度不相符,就需要重新下载
<span>				</span>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();
					}
				}
<span>				</span>// 判断进度是否和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());
		}

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

HttpResponse response = httpClient.execute(requestBase, httpContext);//访问网络

通过  httpclient 去联网请求的  。

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

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

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

	@Override
		public 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 block
					e.printStackTrace();
				} catch (ProtocolException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.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());
		}

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

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());
	  }

如果 文件当前大小为0,或者文件不存在,或者长度不等于当前长度,则重新下载,否则 设置 Range

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

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

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

	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());
	}

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

然后

		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();

很普通的代码,把数据写出去。不断刷新当前进度, 最后关闭流。

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

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

共勉

版权声明:本文为博主原创文章

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 19:05:19

J哥---------Android 多线程下载 仿下载助手(改进版)的相关文章

*Android 多线程下载 仿下载助手(改进版)

首先声明一点: 这里的多线程下载 并非指的 多个线程下载一个 文件.而是 每一个线程 负责一个文件. 真正的多线程 希望后面能给大家带来.  -------------  欢迎 爱学习的小伙伴 加群  -------------  -------------android交流群:230274309------------- -------------一起分享.一起进步! 须要你们-------------- --------------  期待各位爱学习的小伙伴们 的到来------------

J哥------------Android Studio 篇

Android Studio 篇 多说几句,还在用eclipse 的,尽快转过来吧,这个真的比eclipse 好用,而且现在市面上很多公司都用android studio了,并且 google 也准备力推自家的产品了. 谷歌宣布Android Studio将取代Eclipse编译环境 中止对后者支持 最近也是煎熬期,对于Android Studio 这个东西,真是连导入工程,删除工程都要百度. - Android Studio 篇----下载安装 下载地址:http://www.android-

*Android 多线程下载 仿下载助手

今天带来一个多线程下载的 样例.先看一下效果.点击 下载 開始下载,同一时候显示下载进度.完成下载,变成程 安装,点击安装 提示 安装应用. 界面效果 线程池 ThreadPoolExecutor 在以下介绍实现下载原理的时候.我想尝试倒着来说.这样是否好理解一点?     我们都知道.下载助手,比方360, 百度的 手机助手,下载APP 的时候 ,都能够同一时候下载多个.所以,下载肯定是多线程的.所以我们就须要一个线程工具类 来管理我们的线程,这个工具类的核心,就是 线程池. 线程池Threa

Android 多线程断点续传同时下载多个大文件

最近学习在Android环境中一些网络请求方面的知识,其中有一部分是关于网络下载方面的知识.在这里解析一下自己写的demo,总结一下自己所学的知识.下图为demo的效果图,仿照一些应用下载商城在ListView中列出加载项,然后可以可以下载和停止. 1.概述 这里有几个比较重要的类DownloadManager.DownloadService.DownloadTask.ThreadDAOImpl.主要的下载流程如下. (1) DownloadManager 负责下载任务的调配,以及下载服务Dow

android 多线程断点续传下载 四 - 仿下载助手

我们先一起简单回顾下它的基本原理. http://blog.csdn.net/shimiso/article/details/6763664  android 多线程断点续传下载 一 http://blog.csdn.net/shimiso/article/details/6763986  android 多线程断点续传下载 二 http://blog.csdn.net/shimiso/article/details/8448544  android 多线程断点续传下载 三 界面效果 线程池 T

Android多线程断点续传下载

这个月接到一个项目,要写一个像360助手一样的对于软件管理的APP:其中,遇到了一个问题:多线程断点下载 这个 ,由于之前没有写过这方面的应用功能.所以,不免要自学了.然后就在各个昂站上收索并整理了一下.跟大家分享一下,也能加深我在这方面的理解. 什么是多线程下载? 多线程下载其实就是迅雷,BT一些下载原理,通过多个线程同时和服务器连接,那么你就可以榨取到较高的带宽了,大致做法是将文件切割成N块,每块交给单独一个线程去下载,各自下载完成后将文件块组合成一个文件,程序上要完成做切割和组装的小算法

Android 多线程下载原理剖析

今天带来一个多线程下载的 例子.先看一下效果,点击 下载 开始下载,同时显示下载进度,下载完成,变成程 安装,点击安装 提示 安装应用. 界面效果 这里写图片描述 线程池 ThreadPoolExecutor 在下面介绍实现下载原理的时候,我想尝试倒着来说,这样是否好理解一点? 我们都知道,下载助手,比如360, 百度的 手机助手,下载APP 的时候 ,都可以同时下载多个,所以,下载肯定是多线程的,所以我们就需要一个线程工具类 来管理我们的线程,这个工具类的核心,就是 线程池. 线程池Threa

Andoid 更好的Android多线程下载框架

概述 为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛! 本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点: 多线程 多任务 断点续传 支持大文件 可以自定义下载数据库 高度可配置,像超时时间这类 业务数据和下载数据分离 下面我们在说下该框架能实现那些的应用场景: 该框架可以很方便的下载单个文件,并且显示各种状态,包括开始下载,下载中,下载失败,删除等状态. 也可以实现常见的需要下载功能应用,比如:某某手机助手,在该应用内可以说是下载是核心功能,所以对框架的稳定性,代码

无废话Android之smartimageview使用、android多线程下载、显式意图激活另外一个activity,检查网络是否可用定位到网络的位置、隐式意图激活另外一个activity、隐式意图的配置,自定义隐式意图、在不同activity之间数据传递(5)

1.smartimageview使用 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"