SimpleDownload 一款Android下载框架的讲解
-
-
- 一、本框架实现的功能特性
- 二、最终效果图,及使用方法
- 三、关键功能源码说明
-
- 1.文件下载和普通网络请求的不同
- 2. 断点续传如何实现
- 3. 如何得到文件下载进度
- 4. 如何把回调切换到主线程
-
本文介绍自己封装的一套下载框架。
首先要知道,文件下载和我们平时调接口一样,要发起网络请求的,所以我们先找个合适的网络请求库,这里我们选择Retrofit+OkHttp的组合,为了方便支持回调线程切换等功能再加上RxJava的组合。
本框架已更新多次,而本文章可能未及时更新。详细介绍说明请看github
https://github.com/liwuchen/SimpleDownload
一、本框架实现的功能特性
本下载框架实现了这样的特性:
1. 支持断点续传。即下载断开后,支持从已完成的部分开始继续下载。
2. 支持多个任务同时下载。
3. 有各种情况的回调。如下载进度回调,任务取消、完成等回调。
4. 下载任务在子线程进行,回调在主线程。不需要用户自己切换线程。
5. 用户可以自定义文件保存路径和文件名。
二、最终效果图,及使用方法

本框架源码及Demo已上传github,地址:https://github.com/liwuchen/SimpleDownload
如果想在Android Studio项目中使用本框架,可以按如下方式集成:
1.项目根目录下的Build.gradle文件中修改:
allprojects {
repositories {
google()
jcenter()
maven { url 'https://www.jitpack.io' } // 添加这行
}
}
2.模块Build.gradle文件添加:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api 'com.github.liwuchen:SimpleDownload:1.4.0' // 添加这行
}
请使用最新版本。
3.添加权限(读写文件的权限还需动态申请,这里就不贴出了):
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
4.代码中使用起来也很简单:
// 自定义文件夹名
private final String FOLDER_NAME = "SimpleDownload";
// 文件保存路径
private final String SAVE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + FOLDER_NAME;
// 文件下载地址
private final String url = "http://ccr.csslcloud.net/5D2636511DBBCADD/BBD5D1D6504FF2AD9C33DC5901307461/8DE588DAEE2FE914.ccr";
// 保存文件名
private final String fileName = "1.file";
// 定义下载回调
DownloadListener listener = new DownloadListener() {
@Override
public void onStartDownload() {}
@Override
public void onProgress(long downloaded, long total) {}
@Override
public void onPauseDownload() {}
@Override
public void onCancelDownload() {}
@Override
public void onFinishDownload(String savedFile) {}
@Override
public void onFail(String errorInfo) {}
};
DownloadManager downloadManager = DownloadManager.getInstance();
// 开始下载
downloadManager.download(url, SAVE_PATH, fileName, listener);
// 暂停下载
downloadManager.pauseDownload(url);
// 继续下载
downloadManager.continueDownload(url);
// 取消下载,不删除文件
downloadManager.cancelDownload(url, false);
// 取消下载,并删除文件
downloadManager.cancelDownload(url, true);
三、关键功能源码说明
本框架的源码类如下:
1.文件下载和普通网络请求的不同
熟悉Retrofit的同学肯定知道,使用Retrofit发起网络请求,需要编写这样的接口:
@Streaming
@GET
Observable<ResponseBody> rxDownload(@Header("RANGE") String start, @Url String url);
而这里我们加上了@Streaming注解,这表明,请求得到的数据会以流的形式返回,即文件下载。
2. 断点续传如何实现
首先知道,在网络请求时,如果Header加上Range: bytes=xx,则表示从xx个字节开始下载。
那么我们只要继续下载文件时,header里指定开始的位置即可。
本框架里,我们上面贴出的接口里,已含有"RANGE"这个header,使用的时候传入参数即可。
3. 如何得到文件下载进度
本框架使用了自定义拦截器+自定义ResponseBody的方式。
使用拦截器拦截返回的数据,然后将数据转换成自定义ResponseBody。就可以得到下载进度了。
这里要提一下,有的服务器返回数据时,未返回文件的总大小,因此进度回调里返回的总大小是-1,在时用的时候要判断下。
4. 如何把回调切换到主线程
通过RxJava实现,很方便。
如果对RxJava不熟悉的话,可以看我的另一篇关于RxJava的入门讲解:
《RxJava用法入门及操作符讲解,简单易懂》
DownloadService downloadService = retrofit.create(DownloadService.class);
downloadService
.rxDownload("bytes=" + start + "-", url)
// 下载过程在IO线程
.subscribeOn(Schedulers.io())
// 文件转换为字节流,写入文件,也在IO线程
.observeOn(Schedulers.io())
.map(new Function<ResponseBody, InputStream>() {
@Override
public InputStream apply(ResponseBody responseBody) throws Exception {
return responseBody.byteStream();
}
})
.doOnNext(new Consumer<InputStream>() {
@Override
public void accept(InputStream inputStream) throws Exception {
// 保存文件
FileUtils.writeFileFromIS(filePath, fileName, inputStream, newFile);
}
})
// 主线程处理回调
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<InputStream>() {
@Override
public void onSubscribe(Disposable d) {
if (listener != null) {
listener.onStartDownload();
}
downloadInfo.setDisposable(d);
}
@Override
public void onNext(InputStream stream) {
}
@Override
public void onError(Throwable e) {
if (listener != null) {
listener.onFail(e.getMessage());
downloadInfo.setState(DownState.ERROR);
}
}
@Override
public void onComplete() {
downloadMap.remove(downloadInfo.getUrl());
}
});
下面是DownloadProgressListener#progress()方法里监听下载进度及下载完成的回调;
//切换到主线程
Disposable d = Observable.just(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
if (done) {
//下载完成
listener.onFinishDownload(info.getSavePath() + File.separator + info.getFileName());
info.setState(DownState.FINISH);
} else {
//下载进度回调
if (info.getContentLength() > 0) {
listener.onProgress(info.getReadLength(), info.getContentLength());
} else {
// 获取不到文件总大小(contentLength==0)
listener.onProgress(info.getReadLength(), -1);
}
}
}
});
源码已上传:https://github.com/liwuchen/SimpleDownload