基于RxJava2 + Retrofit2 + Greendao实现文件下载:支持多文件、多线程断点续传

项目中经常会有文件下载、版本更新等功能。正好周末无事,用RxJava2 + Retrofit2 + Greendao造了一个文件下载的轮子。效果图如下:在这里插入图片描述

主要功能:

  1. 文件的下载、暂停、删除、恢复下载功能
  2. 多文件的全部开始、全部暂停、全部删除下载任务功能
  3. 支持自定义同时进行下载的最大线程数量:默认3个
  4. 支持自定义本地下载路径
  5. 支持断点续传:库中实现了数据库结构,用于保存每条下载纪录的信息
  6. 封装动态权限帮助类:可直接使用请求相关权限

主要方法

方法名 作用
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
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值