项目中经常会有文件下载、版本更新等功能。正好周末无事,用RxJava2 + Retrofit2 + Greendao造了一个文件下载的轮子。效果图如下:
主要功能:
- 文件的下载、暂停、删除、恢复下载功能
- 多文件的全部开始、全部暂停、全部删除下载任务功能
- 支持自定义同时进行下载的最大线程数量:默认3个
- 支持自定义本地下载路径
- 支持断点续传:库中实现了数据库结构,用于保存每条下载纪录的信息
- 封装动态权限帮助类:可直接使用请求相关权限
主要方法
方法名 | 作用 |
---|---|
getInstance() | 静态方法,以单列方式获取下载帮助类对象 |
setSavePath(String localPath) | 设置文件保存路径 |
setMaxTask(int count) | 设置最大下载进程数(同时可以下载的最大数量,默认三个) |
registerListener(DownloadListener listener) | 注册监听,回掉当前下载状态 |
unRegisterListener() | 解除监听,与registerListener配对使用,页面销毁时调用,防止内存泄漏 |
start(String url) | 开始单个文件下载 |
start(List list) | 开始多个下载任务 |
pause(String url) | 暂停单个下载任务 |
pauseAll() | 暂停所有进行中的任务 |
resume(String url) | 恢复下载任务(暂停状态恢复下载) |
delete(String url) | 删除下载任务 |
deleteAll() | 删除所有下载任务 |
getLocalFilePathFromUrl(String url) | 根据下载链接获取本地存储路径 |
首先引入RxJava、Retrfit、Greendao的相关库:
compile 'org.greenrobot:greendao:3.2.2' // add library
compile 'org.greenrobot:greendao-generator:3.2.2'
compile 'com.squareup.retrofit2:retrofit:2.5.0'
compile "io.reactivex.rxjava2:rxjava:2.2.4"
compile 'io.reactivex.rxjava2:rxandroid:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.5.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
至于Greendao的如何使用,大家可以去查询相关资料,这里不进行过多的阐述。
新建存储下载信息的类DownloadInfo,用于存储文件总大小、已下载大小、本地存储路径、下载链接等字段
@Entity
public class DownloadInfo {
@Id
private Long id;
// 本地存储路径
@Property(nameInDb = "local_path")
private String localPath;
// 文件总长度
@Property(nameInDb = "content_length")
private long contentLength;
// 已下载长度
@Property(nameInDb = "read_length")
private long readLength;
// 文件下载链接
@Property(nameInDb = "url")
private String url;
@Property(nameInDb = "is_complete")
private boolean isComplete;
// 绑定下载服务
@Transient
private DownLoadService service;
@Generated(hash = 1329093281)
public DownloadInfo(Long id, String localPath, long contentLength,
long readLength, String url, boolean isComplete) {
this.id = id;
this.localPath = localPath;
this.contentLength = contentLength;
this.readLength = readLength;
this.url = url;
this.isComplete = isComplete;
}
@Generated(hash = 327086747)
public DownloadInfo() {
}
public String getLocalPath() {
return localPath;
}
public void setLocalPath(String localPath) {
this.localPath = localPath;
}
public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public long getReadLength() {
return readLength;
}
public void setReadLength(long readLength) {
this.readLength = readLength;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public DownLoadService getService() {
return service;
}
public void setService(DownLoadService service) {
this.service = service;
}
@Override
public String toString() {
return "DownloadInfo{" +
"localPath='" + localPath + '\'' +
", contentLength=" + contentLength +
", readLength=" + readLength +
", url='" + url + '\'' +
", service=" + service +
'}';
}
public boolean getIsComplete() {
return this.isComplete;
}
public void setIsComplete(boolean isComplete) {
this.isComplete = isComplete;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
}
新建进度回掉接口DownloadProgressListener
public interface DownloadProgressListener {
/**
* @param read 已下载长度
* @param contentLength 总长度
* @param done 是否下载完毕
*/
void progress(long read, long contentLength, boolean done);
}
自定义Retrofit的返回DownloadResponseBody
public class DownloadResponseBody extends ResponseBody{
protected ResponseBody responseBody;
protected DownloadProgressListener listener;
protected BufferedSource bufferedSource;
public DownloadResponseBody(ResponseBody responseBody, DownloadProgressListener listener)
{
this.responseBody = responseBody;
this.listener = listener;
}
@Nullable
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null)
{
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
// read() returns the number of bytes read, or -1 if this source is exhausted.
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
if (null != listener) {
listener.progress(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
}
return bytesRead;
}
};
}
}
建立Retrofit的请求接口DownLoadService
public interface DownLoadService {
/**
* @param start 从某个字节开始下载数据
* @param url 文件下载的url
* @return Observable
* @Streaming 这个注解必须添加,否则文件全部写入内存,文件过大会造成内存溢出
*/
@Streaming
@GET
Observable<ResponseBody> download(@Header("RANGE") String start, @Url String url);
}
因为Retrofit下载过程中是没有直接的进度回掉的,我们需要写一个进度拦截器
DownloadInterceptor
public class DownloadInterceptor implements Interceptor{
protected DownloadProgressListener listener;
public DownloadInterceptor(DownloadProgressListener listener)
{
this.listener = listener;
}
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
return response.newBuilder().body(new DownloadResponseBody(response.body(), listener)).build();
}
}
构建我们的下载管理类DownloadManager:
public class DownloadManager implements DownloadProgressListener{
private static final String TAG = "DownloadManager";
protected DownloadListener progressObserver;
protected DownloadInfo info;
protected DownLoadSer