内容类应用中图片或文件下载,一般应用中应用更新和升级,这些都是经典的下载场景。下载是项目中基础且重要的模块。
从代码逻辑复用性和人力成本考虑,一直想实现一个纯Dart实现的下载库,作为技术储备。
最近发现了一个纯Dart实现的下载库flutter_download_manager,相对来说各方面还算满足需求,支持断点续传,暂停,取消等我比较看重的功能。但是有些地方还需要改进。
话不多说,首先简单介绍下这个库吧。
版本: 0.5.4
特点:
支持平台: Linux | MacOS | Windows | Android | iOS
var dl = DownloadManager();
var url = "adasdad.com/asda.sdas";
dl.addDownload(url, "./test.sdas");
DownloadTask? task = dl.getDownload(url4);
task?.status.addListener(() {
print(task.status.value);
});
task?.progress.addListener(() {
print(task.progress.value);
});
await dl.whenDownloadComplete(url4);
DownloadTask? task = dl.getDownload(url4);
task?.status.addListener(() {
print(task.status.value);
});
DownloadTask? task = dl.getDownload(url4);
task?.progress.addListener(() {
print(task.progress.value);
});
DownloadTask? task = dl.getDownload(url4);
await task.whenDownloadComplete();
var dl = DownloadManager();
dl.cancelDownload(url5);
var dl = DownloadManager();
dl.pauseDownload(url5);
var dl = DownloadManager();
dl.resumeDownload(url5);
整个核心就类 DownloadManager, 而每个下载任务的抽象是 DownloadTask,所谓 Manager 当然是要管理这些 Task 了。那么如何管理呢? 游离的没法管控,只有先找到才能调配,通过 Map 持有 Task 句柄达到“找到”目的,其中_cache 中以<下载 URL,下载任务>方式在内存中缓存每个任务状态;而_queue 则是新添加的下载任务请求,这两者关系后面流程中会具体讲到。
重点说下 status 和 progress 字段设计,不论是批量下载还是单任务下载,进度监听不是通过传统传入一个回调给 download 或者 addDownload 来进行的,而是用了系统的 ValueNotifier。笔者考虑这样设计原因是配合 flutter 系统提供的 ValueListenerBuilder 更容易组织 UI。(这样的设计是不是看起来更 Dart)
重点说下 cancelToken,该字段在暂停,取消,恢复下载任务实现中起了关键作用。像放出去的风筝,想收回时可以收回。怎么收回呢?通过线,这条线的作用就是 cancelToken。而风筝就像是一个个任务请求,放风筝的人就是 Manager,放风筝这件事就是 Task。
每个请求都必须带个 cancelToken,方便取消请求。(不带线的风筝,难道让你上天?)
图中 DownloadManager 中方法只写了单任务下载相关方法,批量相关方法差不多就省略了,类似(add | pause | cancel | resume | remove ).BatchDownload 等,最终通过循环执行了单实现的方法。
这里不具体阐述代码流程,为方便理解直接拿生活中惯用做事逻辑举例,代码实现可自行查阅,也是按照这个套路来滴,首先有两个集合:
后续简称任务列表均指请求列表。
完成某任务一般流程如下:
流程图如下:
关键是对 DownloadRequest 中 cancelToken 的控制。
暂停任务
恢复任务
取消任务
暂停和取消任务骗谁呢?
一般理解暂停表示之前下载了 50%,恢复后继续从 50%下载;取消表示之前下载 50%点击恢复重头再来。
暂停和取消逻辑除更新状态不一样其他基本一样,是在忽悠我么?
莫慌!在下载时候还有处理呢?
通过上述恢复实现与如下下载中逻辑归纳整个暂停实现流程:
import 'package:dio/dio.dart';
class DownloadRequest {
var cancelToken = CancelToken();
}
------------------------------------------------
import 'package:dio/dio.dart';
class DownloadManager {
var dio = Dio();
Future<void> download(String url, String savePath, cancelToken,
{forceDownload = false}) async {
//...
if(fileExist){
}else(partialFileExist)
{
var response = await dio.download(...);
}else{
var response = await dio.download(...);
}
预告:下一篇将实现 dio 解耦和网络库扩展。
任务管理体现在列表的增删改查; 断点续传体现在 range 设置和文件追加;任务取消单纯通过请求库取消实现。
太棒了!鼓励自己坚持到底。我希望我为你投入的时间增加了一些价值。
本文原创听蝉。