这个项目是什么

RetrofitDownloadManager 解决的是 App 里"多个文件同时下载、中途断了能接着下、进度要实时显示在列表里"这件事。README 给出的技术组合是 RxJava + Retrofit + SqlBrite + SqlDelight,作者另有一篇文章专门讲源码,链接放在仓库里。

断点续传本身不算难,难的是把下载状态持久化,再把状态变化推回界面。这个项目的做法是用数据库存任务和进度,用响应式的方式把数据库变化变成列表更新,整体思路值得参考。

技术组合的分工

  • Retrofit 负责网络请求,下载任务最终都落到它身上;
  • RxJava 串起下载流程和线程切换,也是回调和进度传递的基础;
  • SqlDelight 用 SQL 生成类型安全的 Java 接口,省掉手写 DAO 的重复劳动;
  • SqlBrite 把 SQLite 查询包成可观察对象,数据一变,订阅方就能收到通知。

这四个库各管一段:网络、异步、持久化、状态推送,拼起来正好覆盖一个下载管理器的完整职责。

使用方法

  1. 以 module 形式添加到项目。

  2. 在 Application 里初始化下载器:

DownManager.init(this);
  1. 添加下载任务,参数依次是文件保存路径、完整的下载 URL、任务名称:
//创建基本任务,参数为:文件保存的路径、下载的url要全路径、下载任务的名称
DownInfo downInfo = DownInfo.builder().create(getPath(url), url, name);

//开始下载
DownManager.getInstance().startDown(downInfo);
  1. 查询当前的下载列表:
//查询当前所有的下载
DBUtil.getInstance().getAllDown()
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(adapter);
  1. 设置 View 的回调监听器。README 特别提醒要设置两个:一个在 ViewHolder 初始化时通过 RxBus 挂上,另一个在 onBindViewHolder 时添加。
public ListViewHolder(View itemView) {
    super(itemView);
    // 初始化View

    mBtHandle.setOnClickListener(this);

    //因为(downInfo.downUrl()用来传递进度信息,这里使用两个(downInfo.downUrl()进行标识
    RxBus.with().setEvent(DownManager.DOWN_ADD_SUBSCRIBE)
            .onNext(new Action1<Events<?>>() {
                @Override
                public void call(Events<?> events) {
                    //添加监听器,在列表点击开始回调
                    String link = events.getContent();
                    link = link.replace(DownManager.DOWN_ADD_SUBSCRIBE, "");
                    DownManager.getInstance().addListener(link, listener);
                }
            }).create();
}
@Override
public void onBindViewHolder(final ListViewHolder holder, final int position) {
    //在Holder里面设置数据

    //添加View监听器
    DownManager.getInstance().addListener(downInfo.downUrl(), listener);
}

踩坑与思考

README 强调"要设置两个 View 的回调监听器",这是使用这套代码时最容易漏的地方。一个可能的原因是列表复用:同一个 item 在不同位置会绑定不同的下载任务,只在初始化时挂一次监听,滚动之后进度就会串到别的行上。所以绑定数据时要重新 addListener。

另外,进度是按 downInfo.downUrl() 来对应任务的,README 的注释里写得很直白。用 URL 作为标识,意味着同一个地址在管理器里只对应一份任务状态;如果你的场景需要同一个文件发起多个独立任务,这一点要提前考虑。

最后,这套代码基于 RxJava 1 时代的 Action1、Events 等类型,直接搬到新项目里需要先做一轮升级。

仓库地址:tpnet/RetrofitDownloadManager