在本公司项目中,下载是广告相关业务不可缺少的一个重要功能,然而由于下载部分的源码没有对其他部门公开,我又感觉公司的框架不好用,所以忙里抽闲,花了大概一周时间,打造了一个自己的下载器,我希望我的下载器不只可以下载apk,也可以下载文本文件,音乐mp3,等等任何一个链接可以启动的下载任务,初具雏形。还没有拿到实际项目中检验,有问题希望大家提出帮我一起来完善这个项目,目前已经提交到GitHub,和JitPack,我们公司的项目肯定没机会使用了,希望能帮到需要的同伴们。(2020/12/07,目前已作为B方案将要应用到公司项目中)
GitHub地址 git@github.com:renzhenming/DownloadModel.git
1.引入
在根build.gradle中配置
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
在主工程build.gradle中添加(最新版本见GitHub)
dependencies {
implementation 'com.github.renzhenming:DownloadModel:1.0.9'
}
这样你的项目中就可以使用我的下载器了
2.支持的功能
a.多线程下载
有些大文件下载的需求中,单线程下载无法满足对效率的追求,此时我想尽可能多的利用硬件资源提高下载速度,那我可以选择开启2个线程3个线程甚至10个线程(只要你内核足够)来下载这一个文件,虽然效率不能说成倍提高,但是作用还是很明显的
b.多文件下载
比如应用市场项目中,可能同时点了多个apk一同下载,你不需要关心别的,只需把每个下载链接都丢给我的下载器即可,然后下载器会告诉你下载状态,你根据状态来更新你的界面显示即可其他的一切交给它来处理。你也可以自定义线程池来管理每一个下载任务,设置下载器的产能,这些接口都通过IThreadPool接口开放出来,你的自主性很高。
c.断点下载
下载是一个相对耗时的任务,下载过程中被中断的可能性很高,所以不能说一旦终端我就要从头开始,这是一个很糟糕的情况。所以一个完善的下载器必须具备断点续传的能力,这也是一个基本的功能。下载过程中你可以主动停止一次下载,也可能是系统原因导致下载被迫中断,但是这些在我的下载器中都不是问题,也不需要你去关心,你需要做的就是丢一个链接进去,其他的不用你考虑
d.自定义下载路径
每个项目下载的存储路径都是不同的,所以必须将下载路径的设置接口开放给使用者,不然这就不能称之为一个通用的框架,不用担心IPath接口已经提供了这一点
e.自定义缓存管理
下载过程中的状态信息需要保存起来,以备后边使用,就以华为应用市场为例,我下载一个app,下载过程中我暂停了,退出到后台看了会新闻,等我再次想起来我还有一个任务在下载,然后再次进来的时候,从缓存中拿到上次的下载节点,我才能看到当前的下载进度。ICache把缓存的读取和写入的能力提供给使用者,你只需要实现这个接口,来处理自己的逻辑即可,当然如果你图省事,直接用我默认的缓存工具即可。
f.线程池管理
每个项目选用的线程池策略也是各有不同的,有的需要单线程顺序下载,有的需要多线程并列执行,所以这里把线程池的接口也开放出来,给使用者自定义的能力,就是上边有提到的IThreadPool接口
g.开放的网络接口
我的代码里网络是直接通过HttpUrlConnection实现的,你可能希望通过HttpsUrlConnection实现或者想用第三方的开源网络框架实现,那么你就可以自定义一个类实现IConnection接口来实现自己的网络,像这种更换频率较高的代码逻辑我会努力将接口公开给使用者
3.Api
简单介绍一下下载器主要的api接口,后边文章会简要介绍实现原理
download(String downloadUrl)
这是一个同步方法,可以保证多线程的安全性,下载器支持多种类型的下载,只需要传入一个downloadUrl,这是唯一的输入内容,我公司的下载器功能比较完善,但是有一点,和业务逻辑耦合太严重,这就导致他的通用性不佳,我这里直接传链接,几乎可以说在任何场景都是可以直接拿来用的
pause(String downloadUrl)
同样是同步的,用来暂停一个下载,这里仍然通过传递url来执行,看似不太合理,但其实也是为了框架的通用性考虑。也许你会说这里传唯一id看着更顺眼点,这样说很对,但是要知道,并不是每一个下载对象都有id ,的,如果我只是下载一张图片,你却要我传个id进去,难不成我要造一个出来?
registerObserver(DownloadObserver observer)
注册观察者,在需要根据下载进度更新ui的地方进行注册,在退相关页面的时候移除注册
unregisterObserver(DownloadObserver observer)
移除观察者,防止内存泄漏
4.使用方法
开启下载
DownloadManager.getInstance(context).download(downloadUrl);
暂停下载
DownloadManager.getInstance(context).pause(downloadUrl);
获取当前下载状态
DownloadInfo downloadInfo = mDownloadManager.getDownloadInfo(downloadUrl);
if (downloadInfo == null) {
// 没有下载过
mCurrentState = DownloadManager.STATE_NONE;
mProgress = 0;
} else {
// 之前下载过, 以缓存中的对象的状态为准
mCurrentState = downloadInfo.currentState;
mProgress = downloadInfo.getProgress();
}
监听下载状态
DownloadManager.getInstance(context).registerObserver(new DownloadManager.DownloadObserver() {
@Override
public void onDownloadStateChanged(DownloadInfo info) {
}
@Override
public void onDownloadProgressChanged(DownloadInfo info) {
}
});
移除监听
DownloadManager.getInstance(context).unregisterObserver(downloadObserver);
5.总结
本篇着重使用方面的介绍,后边会进行内部代码的简单分析