Android多线程断点续传实现总结

来源:互联网 发布:windows一键还原按什么 编辑:程序博客网 时间:2024/06/07 23:04

0.Android多线程断点续传下载文件类设计思路:

1.流量控制,获取运营商的接入方式,比如说使用移动网络接入,尽可能的提示用户切换WiFi或提示,限制下载的流量以节省话费。
2. 屏幕锁控制,屏幕锁屏后导致应用会被挂起,当然android提供了PowerManager.WakeLock来控制。
3. 对于断点续传,这要追溯到Http 1.1的特性了,主要是获取文件大小,如果这个无法读取的话,那么就无法断点续传了只能使用chunked模式了,当然获取远程服务器上文件的大小可以通过Http的响应头查找Content-Length。
4. 获取上次文件的更改时间,对于断点续传来说比较有风险的就是 继续下载的文件和早期下载的在server上有变动,这将会导致续传时下载的文件版本和原始的不同,一般有两种解决方法,早期我们配置服务器时通过Last-ModifIEd这个http header获取文件上次修改时间,不过本次android开发网推荐使用更为强大的ETag,ETag一般用于解决同一个URL处理不同返回相应,比如Session认证,多国语言,以及部分黑帽的SEO中。具体的实现大家可以参考RFC文档。
5. 考虑服务器的3xx的返回,对于专业的下载文件服务器会考虑到负载平衡问题,这就涉及到重定向问题,处理重定向使用android的apache库处理比较好。
6. 至于多线程,这里提示大家可能存在独立的线程下载一个文件,和多个线程分块下载单个文件之分,其中后者需要考虑上次下载数据是否存在问题,同时如果服务器不支持文件大小获取,则无法通过分段下载数据,因为不知道如何分段,所以在chunked模式中,只能使用一个线程下载一个文件,而不是多个线程下载一个文件。
7. 下载后的数据效验,可以考虑CRC等方式,当然对于一般的传输只要逻辑不出现问题,基本上不会有偏差。
8. 考虑DRM问题,这个问题在国内用的比较少,而国外的受数字保护的音乐和视频,需要额外的获取证书等。
9. 重试次数,对于一个文件可能在本次网络传输中受到问题,尤其是移动网络,所以可以设置一定的重试次数,让任务单独的走下去。
10. 线程开发方式,这里如果你的Java基础比较好,推荐直接使用Java并发库API比较好,如果过去只做过Java开发使用Thread即可,如果Java技术不过关可以android封装的AsyncTask。
尤其需要注意的是第3点和第6点…

1.测试服务器是否支持断点续传:

看看某个请求的响应头信息里面是否包含:
Accept-Ranges bytes
比如请求hao123,相应头信息里面包含:Accept-Ranges bytes,表明支持断点续传

Accept-Ranges bytesCache-Control max-age=0Content-Encoding gzipContent-Length 59107Content-Type text/html;charset=UTF-8Date Fri, 03 Jan 2014 02:40:27 GMTEtag "1946829081"Expires Fri, 03 Jan 2014 02:40:27 GMTLFY cq01.13Last-Modified Fri, 03 Jan 2014 02:40:00 GMTSFY cq01.14Server BWS/1.0Set-Cookie hz=0; path=/; domain=www.hao123.comVary Accept-Encoding

2.多线程下载的过程

(1)首先获得下载文件的长度,然后设置本地文件的长度。

HttpURLConnection.getContentLength();//获取下载文件的长度RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");file.setLength(filesize);//设置本地文件的长度

(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。

如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。
这里写图片描述
例如10M大小,使用3个线程来下载,
线程下载的数据长度 (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M
下载开始位置:线程id*每条线程下载的数据长度 = ?
下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?

(3)使用Http的Range头字段指定每条线程开始下载位置,下载结束位置

如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止
代码如下:HttpURLConnection.setRequestProperty(“Range”, “bytes=2097152-4194303”);

(4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。

 RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd"); threadfile.seek(2097152);//从文件的什么位置开始写入数据

3.getContentLength() 的值为 -1

在默认情况下,HttpURLConnection 使用 gzip方式获取,文件 getContentLength() 这个方法,每次read完成后可以获得,当前已经传送了多少数据,而不能用这个方法获取 需要传送多少字节的内容,当read() 返回 -1时,读取完成,由于这个压缩后的总长度我无法获取,那么进度条就没法计算值了。要取得长度则,要求http请求不要gzip压缩,具体设置如下:

HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn .setRequestProperty("Accept-Encoding", "identity"); conn.connect();

4.接收到的文件大小与getContentLength() 不一致

Content-Length用于描述HTTP消息实体的传输长度;在HTTP协议中,消息实体长度和消息实体的传输长度是有区别,比如说gzip压缩下,消息实体长度是压缩前的长度,消息实体的传输长度是gzip压缩后的长度。

在具体的HTTP交互中,客户端是如何获取消息长度的呢,主要基于以下几个规则:

响应为1xx,204,304相应或者head请求,则直接忽视掉消息实体内容。
如果有Transfer-Encoding,则优先采用Transfer-Encoding里面的方法来找到对应的长度。比如说Chunked模式。
“如果head中有Content-Length,那么这个Content-Length既表示实体长度,又表示传输长度。如果实体长度和传输长度不相等(比如说设置了Transfer-Encoding),那么则不能设置Content-Length。如果设置了Transfer-Encoding,那么Content-Length将被忽视”。
Range传输。
通过服务器关闭连接能确定消息的传输长度。(请求端不能通过关闭连接来指明请求消息体的结束,因为这样可以让服务器没有机会继续给予响应)。这种情况主要对应为短连接,即非keep-alive模式。
HTTP1.1必须支持chunk模式。因为当不确定消息长度的时候,可以通过chunk机制来处理这种情况。
在包含消息内容的header中,如果有content-length字段,那么该字段对应的值必须完全和消息主题里面的长度匹配。
“The entity-length of a message is the length of the message-body before any transfer-codings have been applied”
也就是有chunk就不能有content-length 。

总结如下:
1、Content-Length如果存在并且有效的话,则必须和消息内容的传输长度完全一致。(经过测试,如果过短则会截断,过长则会导致超时。)

2、如果存在Transfer-Encoding(重点是chunked),则在header中不能有Content-Length,有也会被忽视。

3、如果采用短连接,则直接可以通过服务器关闭连接来确定消息的传输长度。(这个很容易懂)

结合HTTP协议其他的特点,比如说Http1.1之前的不支持keep alive。那么可以得出以下结论:

1、在Http 1.0及之前版本中,content-length字段可有可无。

2、在http1.1及之后版本。如果是keep alive,则content-length和chunk必然是二选一。若是非keep alive,则和http1.0一样。content-length可有可无。

5.多线程下载的时候每个线程都下载完整个文件

比如有3个线程,整个文件的大小为5M,下载回来的时候发现已经下载了15M,但是在文件管理器中看到
的文件却不是15M,而是8M多,这说明http协议有问题.多线程可能有问题.
DownloadThread: Thread 2 download finish
TAG: onDownload--------Size=: 15827952
System.out: 下载完毕!!!!!!!!!!!

6.机型适配的问题

notification没有进度更新,下载进程也没有进行回调,目前在魅族4上发现这个问题,导致无法下载更新…
(目前无解)

7.自定义请求头获取

比如获取content_length出问题的时候,可以在服务器自定义一个字段,返回实体的长度,客户端根据
字段进行获取,例如:
this.fileSize = conn.getContentLength();//根据响应获取文件大小
改为:
this.fileSize=conn.getHeaderFieldInt("MyApk_Length",-1);
其实就是getContentLength()的底层实现.

8.更新软件的时候出现”应用程序未安装提示”

当下载的时候下载的是apk时,由于软件的签名不一致导致安装失败,需要卸载原安装包,再重新安装即可.

9.安装apk出现”解析程序包时出现问题”

下载过程中,安装包已经损坏……下载过程有问题,特别是多线程和断点的下载,容易造成文件损坏.

10.通知栏的进度和文字提示不更新

这涉及到通知栏的问题,应该是进度条没有刷新UI,一定要调用notify方法:

contentView.setTextViewText(R.id.tv_progress, percent + "%");contentView.setProgressBar(R.id.progressbar, apkSize, haveDown, false);mNotificationManager.notify(NOTIFY_ID, mNotification);

拓展阅读:
Android Netroid解析之——断点续传下载及问题修正
第三方库 AigeStudio/MultiThreadDownloader
Android DownloadManager 的使用
Android 中 DownLoadManager 实现文件下载

1 0
原创粉丝点击