okhttp-OkGo的文件下载模块

OkDownload主要功能

  • 结合OkGo的request进行网络请求,支持与OkGo保持相同的配置方法和传参方式
  • 支持断点下载,支持突然断网,强杀进程后,继续断点下载
  • 每个下载任务具有无状态、下载、暂停、等待、出错、完成共六种状态
  • 所有下载任务按照tag区分,切记不同的任务必须使用不一样的tag,否者断点会发生错乱
  • 相同的下载url地址,如果使用不一样的tag,也会认为是两个下载任务
  • 不同的下载url地址,如果使用相同的tag,会认为是同一个任务,导致断点错乱
  • 默认同时下载数量为3个,默认下载路径/storage/emulated/0/download,下载路径和下载数量都可以在代码中配置
  • 下载文件名可以自己定义,也可以不传,让框架自动获取文件名

注意!!!

OkGo与OkDownload的区别就是,OkGo只是简单的做一个下载功能,不具备断点下载,暂停等操作,但是这在很多时候已经能满足需要了。
而有些app需要有一个下载列表的功能,就像迅雷下载一样,每个下载任务可以暂停,可以继续,可以重新下载,可以有下载优先级,这时候OkDownload就有用了。

OkDownload只是对OkGo功能的一个扩展升级,如果你不需要使用下载管理的功能,那么就不需要导入该包。

废话不多说,看看怎么用。

全局配置

直接上代码,如图: 

  1. 支持设置全局下载文件夹,以后每个任务如果不设置文件夹,将默认用这个路径,如果不设置,默认就是图中的目录。
  2. 可以设置同时下载的任务数量,如果不设置,默认就是3个,改方法只在第一次调用时生效,以后无效
  3. 可以设置所有任务的监听,比如全部任务开始的时候做点事情,全部完成后做点事情。
  4. 记得监听可以取消,否则可能会发生内存泄露。

此外,OkDownload不仅有全局相关的设置,还有对全部任务的同时操作能力。 例如如下方法:

  1. startAll():开始所有任务,或者继续下载所有暂停的任务都是这个方法
  2. pauseAll():将全部下载中的任务暂停
  3. removeAll():移除所有任务,无论这个任务是在下载中、暂停、完成还是其他任何状态,都可以直接移除这个任务,他有一个重载方法,接受一个boolen参数,true表示删除任务的时候同时删除文件,false表示只删除任务,但是文件保留在手机上。不传的话,默认为false,即不删除文件。
  4. removeTask():根据tag移除任务
  5. getTaskMap():获取当前所有下载任务的map
  6. getTask():根据tag获取任务
  7. hasTask():标识为tag的任务是否存在

Progress对象

在使用之前,先介绍一个对象,这个对象是我们在做进度回调时,无论是上传进度回调,还是下载进度回调,无论是使用OkGoOkDownload,或则OkUpload时候,都会碰到一个对象,叫Progress对象,他保存了当前进度的很多信息,源码中的定义如下,字段很多,但是我觉得看字段名和后面的注释就能知道是什么作用了,所以如果你需要什么进度信息,直接就能取出来。

唯一需要说明的一点是,最顶部对应的6个静态常量,表示下载的状态,其实就是对应下面的status字段的,该字段永远是这六个状态中的一个。 

基本断点下载方法

我们首先看看一个最基本的下载任务是怎么写的,先给代码如下: 以上的几行代码就完成了下载文件的断点下载,可以开始,暂停,继续,重新下载等,是不是很简单,关于以上代码,详细说明如下:

  1. 构建一个下载请求Request,这个构建方法和OkGo是一样的,params参数和headers参数是只是演示使用,一切OkGo的使用方法,这里都是一样的。
  2. 构建下载任务,使用OkDownload中的request方法,传入一个tag和我们上一步创建的request对象,创建出下载任务,其他的方法我们下文在细讲。
  3. 启动任务,我们已经得到了DownloadTask任务对象,那么简单调用start启动他就好了,同时他还支持这么几个方法:
    • start():开始一个新任务,或者继续下载暂停的任务都是这个方法
    • pause():将一个下载中的任务暂停
    • remove():移除一个任务,无论这个任务是在下载中、暂停、完成还是其他任何状态,都可以直接移除这个任务,他有一个重载方法,接受一个boolen参数,true表示删除任务的时候同时删除文件,false表示只删除任务,但是文件保留在手机上。不传的话,默认为false,即不删除文件。
    • restart():重新下载一个任务。重新下载会先删除以前的任务,同时也会删除文件,然后从头开始重新下载该文件。

下载任务需要一个回调,我们上面随意注册一个打日志的回调,代码如下: 控制台的输出如下: 

这个log要做以下几点说明:

  1. onStart()方法是在下载请求之前执行的,所以可以做一些请求之前相关的事,比如修改请求参数,加密,显示对话框等等。
  2. 服务端返回的响应码是206,注意是206,这个很重要,只有206才能实现断点下载,表示本次返回的是部分响应体,并不是全部的数据。
  3. 服务端一定要返回Content-Length,注意,是一定要返回Content-Length这个响应头,如果没有,该值默认是-1,这个值表示当前要下载的文件有多大,如果服务端不给的话,客户端在下载过程中是不可能知道我要下载的文件有多大的,所以常见的问题就是进度是负数。
  4. 下载文件的时候,响应体会打印一句话:maybe [binary boby], emitted!,这句话表示当前的数据是二进制文件,控制台没法打印也没必要打印出来,所以不用打印了。很多人经常拿着这个log告诉我出问题了,搞的我真是欲哭无泪,所以不要认为是bug,这是正常的。
  5. 下载完后,最后会调用onFinish(),不过我设计成在调用onFinish()之前,还会额外调用一次onProgress()方法,这样的好处可以在onProgress方法中捕获到所有的状态变化,方便管理。

下载API介绍

以上的介绍只是简单的说了一下使用方法,那么OkDownload究竟提供的api一共有哪些呢,我们来看看,保证满足你的任何需求。

Request的构建详细参考OkGo的用法,这里重点介绍DownloadTask的构建,这里面的方法一个个介绍:

  1. request():静态方法创建DownloadTask对象,接受两个参数,第一个参数是tag,表示当前任务的唯一标识,就像介绍中说的,所有下载任务按照tag区分,不同的任务必须使用不一样的tag,否者断点会发生错乱,如果相同的下载url地址,如果使用不一样的tag,也会认为是两个下载任务,不同的下载url地址,如果使用相同的tag,也会认为是同一个任务,导致断点错乱。切记,切记!!
  2. priority():表示当前任务的下载优先级,他是一个int类型的值,只要在int的大小范围内,数值越大,优先级越高,也就会优先下载。当然也可以不设置,默认优先级为0,当所有任务优先级都一样的时候,就会按添加顺序下载。
  3. floder():单独指定当前下载任务的文件夹目录,如果你是6.0以上的系统,记得下载的时候先自己获取sd卡的运行时权限,否则文件夹创建不成功,无法下载。当然也可以不指定,默认下载路径/storage/emulated/0/download
  4. fileName():手动指定下载的文件名,一般来说是不需要手动指定的,也建议不要自己指定,除非你明确知道你要下载的是什么,或者你想改成你自己的文件名。如果不指定,文件名将按照以下规则自动获取,获取规则与OkGo文件下载获取规则一致,点击这里查看
  5. extra():这个方法相当于数据库的扩展字段,我们知道我们断点下载是需要保存下载进度信息的,而我们这个框架是保存在数据库中,数据库的字段都是写死的,如果用户想在我们的下载数据库中保存自己的数据就做不到了,所以我们这里提供了三个扩展字段,允许用户保存自己想要的数据,如果不需要的话,也不用调用该方法。
  6. register():这是个注册监听的方法,我们既然要下载文件,那么我们肯定要知道下载的进度和状态是吧,就在这里注册我们需要的监听,监听可以注册多个,同时生效,当状态发生改变的时候,每个监听都会收到通知。当然如果你只是想下载一个文件,不关心他的回调,那么你不用注册任何回调。

细心你一定会发现我有个方法没有讲,那就是save()方法,这个方法很重要,重要到我要单独拿出来说他。我们先看看这个方法的源码: 简直简单到不行,就一行代码,就是把当前的progress进度对象,保存到数据库。

想想,如果数据库中没有你这条记录,然后你还开始下载,我如何去更新这个记录,一旦你这么做,OkDownload就会抛出一个异常如下,告诉你必须在start()前,先调用save()方法。 所以我们知道了,save()用在什么地方呢?

  1. 如果你是第一次下载这个标识为这个tag的任务,那么你一定要调用save()方法,先将该数据写入数据库。
  2. 如果你对标识为tag的任务进行了一些参数的修改,比如修改了extra数据,修改了下载目录,下载文件夹等等,也必须调用save()方法,更新数据库。

到这里就基本把请求的相关api介绍完了。

ProgressListener对象

我们上面介绍了一些请求的API,这里我们看看回调的API,下载回调用的DownloadListener,源码如下: 实现了一个接口,定义如下: 需要说明的是以下几点:

  1. DownloadListener的构造方法需要传一个tag,这个tag唯一标识当前listener,主要目的是方便取消监听,同时可以防止数据错乱。
  2. 你看到的这5个回调方法,全部在主线程回调,不需要你自己开任何线程。
  3. onProgress方法不仅在任何进度变化的时候会被回调,任何下载状态变化的时候也会回调,所以很多时候,想监听状态变化,也在这个方法中就够了。

监听取消

我们都知道,网络请求是个长时间的事情,如果你在下载过程中,关闭了Activity,而你的回调又还在这个页面中更新UI,那么久会发生内存泄露,为了避免这种情况的发生,推荐以下做法:

  1. 如果你要实现页面进度的实时刷新,那么该监听就只需要在当前页面有效就行了,关闭页面之前,记得把监听移除,这样就不会泄露了,代码如下,我们看到了取消监听的时候需要传递一个tag,那么这个tag是你创建监听的时候传递的那个tag,不是创建下载任务的那个task,不要搞混了。监听是监听的,任务是任务的。 
  2. 有人问了,我要是取消了,我想在其他页面,其他地方监听到这个下载任务怎么办?你当然可以继续注册一个监听,这个监听里面不要干与UI相关的事,不要持有Activity的引用,那么你这个监听是不会造成泄露的。
  3. 如果你还不放心,你可以自己额外起一个service,在service中下载任务,注册监听,这样就不怕泄露了。

DownloadManager

我们知道了下载任务是保存在数据库中的,那么数据库中的数据我们能不能直接查看呢,答案是可以的,就是这DownloadManager类,这个类是对下载任务的数据库进行增删改查的管理类,切记不要自己修改这里面的数据,不要直接使用DownloadManager的api,而应该使用OkDownload的api,这样才能确保数据的完整性和准确性。

介绍3个常用的方法: 分别表示从数据库中获取所有的下载记录,已完成的下载记录和未完成的下载记录。

这里可以看出来,我们获取是Progress对象的列表,我们拿到这个列表一般是没什么用的,我们也不能去修改这里面的数据。

所以,这个类的方法一般还需要和OkDownload的方法配合一下使用,一般用法就是获取数据后,将数据库的集合数据恢复成下载任务数据,代码如下: 

这里我们看到了这个OkDownload.restore()这个方法,他的作用就是把数据库中的记录,恢复成下载任务。我们拿到了这个任务列表,这个DownloadTask对象,操作方法不就是文档上面讲的那堆了不。

结束

到这里OkDownload的用法就和全部API基本就讲完了。

什么?你说你用的时候列表进度错乱?这个怪我喽?

关于在列表中进度错乱的问题,这个其实和库一点关系没有,完全是因为ListView或者RecyclerView复用问题导致的,你要是不会解决,好好看看Demo怎么解决的吧。

原文地址:https://www.cnblogs.com/fuyaozhishang/p/8285349.html