Retrofit2 的再封装实战—多线程下载与断点续传 (一)

3,066 阅读5分钟
原文链接: www.jianshu.com

前言

先感谢大家对第一篇文章《Retrofit2的再封装实战—同步与异步请求
的支持,提笔之前反复考虑了很多,要怎么写好多线程下载和断点续传?倒不是因为逻辑有多复杂,是因为这里覆盖的知识面太多了,大量的多线程共享数据,本地数据持久化,以及面向不同状态如何反馈的问题,这些东西每一块其实都能掰碎了写一写,所以拼在一起,让我不知道到底应该从哪里入手开始写。
既然上篇文章结尾说过了如果有人需要就分享这方面的封装,为了不让期待的朋友们失望。最后决定还是要硬着头皮开始写,至于从哪里开始写,既然没有好的想法,那就从头开始,就当是重新写一遍吧,如果在这过程中你有不通或者不认可的想法,欢迎分享指正给我,大家一起进步吧。

(这篇文章是基于我的第一篇文章再拓展的,所以建议您先移步上篇文章)

视频演示:


monkey.gif


点下载有等待时间,是因为我设置了每下载1m才回调。

正题

基础概念

一、什么是多线程下载?

Android主线程是不建议阻塞的,通常的思路,我们下载文件,都是在线程中执行,一个线程对应一个下载通道,如果文件有上百M大小,况且你的网络状态不够好,应该怎么办呢?想一下如果我们把一个100M的文件,拆成4个线程,同时下载,那该是多么幸福的事情啊。无论是效率还是时间上,都会有大幅度的提升。那么在http上怎么实现分段下载呢?很简单 在请求头上加入Range属性,像这样设置bytes范围就可以了。


Range.png


现在Response Body就会返回对应范围的字节流了。好了,现在我们来拓展NetWorkRequest类,加入Download接口。
public interface DownLoadService { @Streaming @GET Call downloadFile(@Url String fileUrl,@Header("Range") String range); }
在NetWorkRequest中的中加入获得DownLoadService接口代理类的方法,在init中初始化DownLoadService:
mDownLoadService = mRetrofit.create(DownLoadService.class);
向外提供mDownLoadService引用:
public DownLoadService getDownLoadService() { return mDownLoadService; }
好了,关于DownLoadService的初始化就结束了。

二、什么是断点续传?

简单解释就是 从哪摔倒的接着从哪站起来 这就意味着我们可以做取消、继续下载的功能,或者从异常情况下恢复的再下载功能。
关于如何保持下载数据问题,其实就是如何选择数据持久化方式的问题。
Android数据持久化方式 Preferences SQL I/O ContentProvider,这里选择SQL应该是没有任何异议的,至于你是选择如何实现SQL功能,GreenDao还是Realm或者其他开源项目,这里不做推荐,只要能够实现简单的增删改查功能就好,我选择了原生的sqllite。只为了实现简单功能,并未做完整封装,不考虑借鉴我的sqllite。

程序结构

介绍了基本概念的理解,相信大家对本次实战的内容应该有大体了解了。下面上简单的项目结构图并说一下思路:


结构图.jpeg


Action:为行动的意思,这里代表我们的Activity,Fragment,或者是Service。A、B即代表着两次请求行为。

DownLoadManager:不同的Action,通过统一入口DownLoadManager调用下载功能,DownLoadManager是单列的。只提供唯一入口,就像很多开源框架Glide.with()、ImageLoader.load()一样,这里是一个设计思路,外观模式,也称为门面模式,大家设计框架时候尽量这样来做,只提供一个入口,即为了以后好维护,也减少了耦合性。我们里面代码再怎么修改,也并不影响外面的调用。不要改一动全身那就尴尬了。DownLoadManager提供创建createRequest方法,我们把每个Action的所有下载任务封装为一个Request,DownLoadManager中只负责创建和取消下载任务,并维护下载任务的缓存。

Request:上面说道,Request即为一次下载任务。每个Request里面可能有几十,甚至上百个url,每个url可能又会因为数据太大而分为多线程任务。每个Request里面维护着所有任务的缓存,基本的逻辑判断,数据持久化都在Request里面,所以这是最有含金量的部分,怎么做到性能最优,怎么做到多线程数据的安全性,都在这里面。

Task:每个url即为一个Task,通过Retrofit调用DownLoad接口,并进行数据存储操作,这里并没有什么难度,只需要通过判断不用的情况,返回不通的状态值,对数据进行不同的操作。同时,根据你的业务逻辑,来适配不通IOException行为。

简单来讲,入口类DownLoadManager,调用者只需要关心这一个类,创建下载任务,DownLoadManager把每个任务封装成一个Request对象,复杂的逻辑判断全部交给Request来做,Task是真正执行下载任务的对象,数据回调从下向上依次传递回Action。

文章计划

写到这里,我想我介绍的思路应该已经很清晰了,本次内容分为三个部分介绍。第一部分到这就结束了,只要是讲一下概念和思路和DownloadService的初始化,觉得有意思的朋友可以根据我上文说的思路,设计一下。下面两篇文章是这样计划的,第二篇从DownloadTask和DownloadManager入口,最后一篇放到重要的Request上,如果您感兴趣,可以关注下这个系列。