Android 文件下载引擎,稳定、高效、灵活、简单易用
[![Gitter][gitter_svg]][gitter_url] [![Download][bintray_svg]][bintray_url] ![][file_downloader_svg] [![Build Status][build_status_svg]][build_status_link] ![][filedownloader_snapshot_svg]
现在, FileDownloader2-OkDownload 已经正式发布, okdownload继承了所有FileDownloader的优点,甚至做了更多的优化以及更多的特性。
由于FileDownloader的单元测试覆盖太低,因此所有的进一步的需求以及提高都将会在okdownload上进行实现而非FileDownloader,而FileDownloader本身将只会关注于修复Bug。
ConnectionCountAdapter定制)=2_147_483_647 = 1.99GB)的时候, 请使用FileDownloadLargeFileListener而不是FileDownloadListener(同理使用getLargeFileSofarBytes()与getLargeFileTotalBytes())filedownloader.properties将process.non-separate置为true,可以有效减少每次回调IPC带来的I/O。从 Android 8.0 开发,后台服务的限制增强了,可以参考这里了解更多信息。 因此,自 FileDownloader 1.7.6 版本开始, Android 8.0 及之后的系统上,如果在后台启动下载服务,这个服务将会是一个前台服务,同时你会看到一个标题为 "FileDownloader" 的通知。 你可以参考这里去自定义通知的内容。
从 Android 9.0 (API level 28) 开始,明文请求默认被禁止,你可以在这里了解详细信息。 FileDownloader 1.7.6 已经在 demo 中处理了此问题。
根据迁移笔记,FOREGROUND_SERVICE 这个权限已经在 1.7.6 版本添加到 library 的 manifest 里面了。
![][single_demo_gif] ![][chunked_demo_gif] ![][serial_tasks_demo_gif] ![][parallel_tasks_demo_gif] ![][tasks_manager_demo_gif] ![][mix_tasks_demo_gif] ![][avoid_drop_frames_1_gif] ![][avoid_drop_frames_2_gif]
在项目中引用:
implementation 'com.liulishuo.filedownloader:library:1.7.7'
如果是eclipse引入jar包参考: 这里
如果需要引入snapshot版本,请添加sonatype的仓库:
repositories {
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
如果你需要注册你的定制组件,你需要在Application#onCreate中调用FileDownloader.setupOnApplicationOnCreate(application):InitCustomMaker, 否则你只需要在使用FileDownloader之前的任意时候调用FileDownloader.setup(Context)即可。
这些初始化方法都十分的简单,不会启动下载服务,一般都是在10ms内完成。
FileDownloader.getImpl().create(url)
.setPath(path)
.setListener(new FileDownloadListener() {
@Override
protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void connected(BaseDownloadTask task, String etag, boolean isContinue, int soFarBytes, int totalBytes) {
}
@Override
protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void blockComplete(BaseDownloadTask task) {
}
@Override
protected void retry(final BaseDownloadTask task, final Throwable ex, final int retryingTimes, final int soFarBytes) {
}
@Override
protected void completed(BaseDownloadTask task) {
}
@Override
protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void error(BaseDownloadTask task, Throwable e) {
}
@Override
protected void warn(BaseDownloadTask task) {
}
}).start();
final FileDownloadListener queueTarget = new FileDownloadListener() {
@Override
protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void connected(BaseDownloadTask task, String etag, boolean isContinue, int soFarBytes, int totalBytes) {
}
@Override
protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void blockComplete(BaseDownloadTask task) {
}
@Override
protected void retry(final BaseDownloadTask task, final Throwable ex, final int retryingTimes, final int soFarBytes) {
}
@Override
protected void completed(BaseDownloadTask task) {
}
@Override
protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) {
}
@Override
protected void error(BaseDownloadTask task, Throwable e) {
}
@Override
protected void warn(BaseDownloadTask task) {
}
};
// 第一种方式 :
//for (String url : URLS) {
// FileDownloader.getImpl().create(url)
// .setCallbackProgressTimes(0) // 由于是队列任务, 这里是我们假设了现在不需要每个任务都回调`FileDownloadListener#progress`, 我们只关系每个任务是否完成, 所以这里这样设置可以很有效的减少ipc.
// .setListener(queueTarget)
// .asInQueueTask()
// .enqueue();
//}
//if(serial){
// 串行执行该队列
// FileDownloader.getImpl().start(queueTarget, true);
// }
// if(parallel){
// 并行执行该队列
// FileDownloader.getImpl().start(queueTarget, false);
//}
// 第二种方式:
final FileDownloadQueueSet queueSet = new FileDownloadQueueSet(downloadListener);
final List<BaseDownloadTask> tasks = new ArrayList<>();
for (int i = 0; i < count; i++) {
tasks.add(FileDownloader.getImpl().create(Constant.URLS[i]).setTag(i + 1));
}
queueSet.disableCallbackProgressTimes(); // 由于是队列任务, 这里是我们假设了现在不需要每个任务都回调`FileDownloadListener#progress`, 我们只关系每个任务是否完成, 所以这里这样设置可以很有效的减少ipc.
// 所有任务在下载失败的时候都自动重试一次
queueSet.setAutoRetryTimes(1);
if (serial) {
// 串行执行该任务队列
queueSet.downloadSequentially(tasks);
// 如果你的任务不是一个List,可以考虑使用下面的方式,可读性更强
// queueSet.downloadSequentially(
// FileDownloader.getImpl().create(url).setPath(...),
// FileDownloader.getImpl().create(url).addHeader(...,...),
// FileDownloader.getImpl().create(url).setPath(...)
// );
}
if (parallel) {
// 并行执行该任务队列
queueSet.downloadTogether(tasks);
// 如果你的任务不是一个List,可以考虑使用下面的方式,可读性更强
// queueSet.downloadTogether(
// FileDownloader.getImpl().create(url).setPath(...),
// FileDownloader.getImpl().create(url).setPath(...),
// FileDownloader.getImpl().create(url).setSyncCallback(true)
// );
}
// 最后你需要主动调用start方法来启动该Queue
queueSet.start()
// 串行任务动态管理也可以使用FileDownloadSerialQueue。
FileDownloader)所有的暂停,就是停止,会释放所有资源并且停到所有相关线程,下次启动的时候默认会断点续传
| 方法名 | 备注 |
|---|---|
| setup(Context) | 如果不需要注册定制组件,就使用该方法在使用下载引擎前调用,该方法只会缓存Context |
| setupOnApplicationOnCreate(application):InitCustomMaker | 如果需要注册定制组件,就在Application#onCreate中调用该方法来注册定制组件以及初始化下载引擎,该方法不会启动下载服务 |
| create(url:String) | 创建一个下载任务 |
| start(listener:FileDownloadListener, isSerial:boolean) | 启动是相同监听器的任务,串行/并行启动 |
| pause(listener:FileDownloadListener) | 暂停启动相同监听器的任务 |
| pauseAll(void) | 暂停所有任务 |
| pause(downloadId) | 暂停downloadId的任务 |
| clear(downloadId, targetFilePath) | 强制清理ID为downloadId的任务在filedownloader中的数据 |
| getSoFar(downloadId) | 获得下载Id为downloadId的soFarBytes |
| getTotal(downloadId) | 获得下载Id为downloadId的totalBytes |
| bindService(void) | 主动启动下载进程(可事先调用该方法(可以不调用),保证第一次下载的时候没有启动进程的速度消耗) |
| unBindService(void) | 主动关停下载进程 |
| unBindServiceIfIdle(void) | 如果目前下载进程没有任务正在执行,则关停下载进程 |
| isServiceConnected(void) | 是否已经启动并且连接上下载进程(可参考任务管理demo中的使用) |
| getStatusIgnoreCompleted(downloadId) | 获取不包含已完成状态的下载状态(如果任务已经下载完成,将收到INVALID) |
| getStatus(id:int, path:String) | 获取下载状态 |
| getStatus(url:String, path:String) | 获取下载状态 |
| setGlobalPost2UIInterval(intervalMillisecond:int) | 为了避免掉帧,这里是设置了最多每interval毫秒抛一个消息到ui线程(使用Handler),防止由于回调的过于频繁导致ui线程被ddos导致掉帧。 默认值: 10ms. 如果设置小于0,将会失效,也就是说每个回调都直接抛一个消息到ui线程 |
| setGlobalHandleSubPackageSize(packageSize:int) | 为了避免掉帧, 如果上面的方法设置的间隔是一个小于0的数,这个packageSize将不会生效。packageSize这个值是为了避免在ui线程中一次处理过多回调,结合上面的间隔,就是每个interval毫秒间隔抛一个消息到ui线程,而每个消息在ui线程中处理packageSize个回调。默认值: 5 |
| enableAvoidDropFrame(void) | 开启 避免掉帧处理。就是将抛消息到ui线程的间隔设为默认值10ms, 很明显会影响的是回调不会立马通知到监听器(FileDownloadListener)中,默认值是: 最多10ms处理5个回调到监听器中 |
| disableAvoidDropFrame(void) | 关闭 避免掉帧处理。就是将抛消息到ui线程的间隔设置-1(无效值),这个就是让每个回调都会抛一个消息ui线程中,可能引起掉帧 |
| isEnabledAvoidDropFrame(void) | 是否开启了 避免掉帧处理。默认是开启的 |
| startForeground(id:int, notification:Notification) | 设置FileDownloadService为前台模式,保证用户从最近应用列表移除应用以后下载服务不会被杀 |
| stopForeground(removeNotification:boolean) | 取消FileDownloadService的前台模式 |
| setTaskCompleted(url:String, path:String, totalBytes:long) | 用于告诉FileDownloader引擎,以指定Url与Path的任务已经通过其他方式(非FileDownloader)下载完成 |
| setTaskCompleted(taskAtomList:List) | 用于告诉FileDownloader引擎,指定的一系列的任务都已经通过其他方式(非FileDownloader)下载完成 |
| setMaxNetworkThreadCount(int) | 设置最大并行下载的数目(网络下载线程数), [1,12] |
| clearAllTaskData() | 清空filedownloader数据库中的所有数据 |
InitCustomMaker)| 方法名 | 需实现接口 | 已有组件 | 默认组件 | 说明 |
|---|---|---|---|---|
| database | FileDownloadDatabase | RemitDatabase、SqliteDatabaseImpl、NoDatabaseImpl | RemitDatabase | 传入定制化数据库组件,用于存储用于断点续传的数据 |
| connection | FileDownloadConnection | FileDownloadUrlConnection | FileDownloadUrlConnection | 传入定制化的网络连接组件,用于下载时建立网络连接 |
| outputStreamCreator | FileDownloadOutputStream | FileDownloadRandomAccessFile | FileDownloadRandomAccessFile | 传入输出流组件,用于下载时写文件使用 |
| maxNetworkThreadCount | - | - | 3 | 传入创建下载引擎时,指定可用的下载线程个数 |
| ConnectionCountAdapter | ConnectionCountAdapter | DefaultConnectionCountAdapter | DefaultConnectionCountAdapter | 根据任务指定其线程数 |
| IdGenerator | IdGenerator | DefaultIdGenerator | DefaultIdGenerator | 自定义任务Id生成器 |
- 如果你希望Okhttp作为你的网络连接组件,可以使用这个库。
- 如果你不希望FileDownloader用到任何的数据库(是用于存储任务的断点续成信息的),只需要使用NoDatabaseImpl.java即可。
| 方法名 | 备注 |
|---|---|
| setPath(path:String) | 下载文件的存储绝对路径 |
| setPath(path:String, pathAsDirectory:boolean) | 如果pathAsDirectory是true,path就是存储下载文件的文件目录(而不是路径),此时默认情况下文件名filename将会默认从response#header中的contentDisposition中获得 |
| setListener(listener:FileDownloadListener) | 设置监听,可以以相同监听组成队列 |
| setCallbackProgressTimes(times:int) | 设置整个下载过程中FileDownloadListener#progress最大回调次数 |
| setCallbackProgressIgnored() | 忽略所有的FileDownloadListener#progress的回调 |
| setCallbackProgressMinInterval(minIntervalMillis:int) | 设置每个FileDownloadListener#progress之间回调间隔(ms) |
| setTag(tag:Object) | 内部不会使用,在回调的时候用户自己使用 |
| setTag(key:int, tag:Object) | 用于存储任意的变量方便回调中使用,以key作为索引 |
| setForceReDownload(isForceReDownload:boolean) | 强制重新下载,将会忽略检测文件是否健在 |
| setFinishListener(listener:FinishListener) | 结束监听,仅包含结束(over(void))的监听 |
| setAutoRetryTimes(autoRetryTimes:int) | 当请求或下载或写文件过程中存在错误时,自动重试次数,默认为0次 |
| setSyncCallback(syncCallback:boolean) | 如果设为true, 所有FileDownloadListener中的回调都会直接在下载线程中回调而不抛到ui线程, 默认为false |
| addHeader(name:String, value:String) | 添加自定义的请求头参数,需要注意的是内部为了断点续传,在判断断点续传有效时会自动添加上(If-Match与Range参数),请勿重复添加导致400或其他错误 |
| addHeader(line:String) | 添加自定义的请求头参数,需要注意的是内部为了断点续传,在判断断点续传有效时会自动添加上(If-Match与Range参数),请勿重复添加导致400或其他错误 |
| setMinIntervalUpdateSpeed(minIntervalUpdateSpeedMs:int) | 设置下载中刷新下载速度的最小间隔 |
| removeAllHeaders(name:String) | 删除由自定义添加上去请求参数为{name}的所有键对 |
| setWifiRequired(isWifiRequired:boolean) | 设置任务是否只允许在Wifi网络环境下进行下载。 默认值 false |
| asInQueueTask(void):InQueueTask | 申明该任务将会是队列任务中的一个任务,并且转化为InQueueTask,之后可以调用InQueueTask#enqueue将该任务入队以便于接下来启动队列任务时,可以将该任务收编到队列中 |
| start(void) | 启动孤立的下载任务 |
| pause(void) | 暂停下载任务(也可以理解为停止下载,但是在start的时候默认会断点续传) |
| getId(void):int | 获取唯一Id(内部通过url与path生成) |
| getUrl(void):String | 获取下载连接 |
| getCallbackProgressTimes(void):int | 获得progress最大回调次数 |
| getCallbackProgressMinInterval(void):int | 获得每个progress之间的回调间隔(ms) |
| getPath(void):String | 获取文件路径 或 文件目录 |
| isPathAsDirectory | 判断getPath()返回的路径是文件存储目录(directory),还是文件存储路径(directory/filename) |
| getTargetFilePath | 获取目标文件的存储路径 |
| getListener(void):FileDownloadListener | 获取监听器 |
| getSoFarBytes(void):int | 获取已经下载的字节数 |
| getTotalBytes(void):int | 获取下载文件总大小 |
| getStatus(void):int | 获取当前的状态 |
| isForceReDownload(void):boolean | 是否强制重新下载 |
| getEx(void):Throwable | 获取下载过程抛出的Throwable |
| isReusedOldFile(void):boolean | 判断是否是直接使用了旧文件(检测是有效文件),没有启动下载 |
| getTag(void):Object | 获取用户setTag进来的Object |
| getTag(key:int):Object | 根据key获取存储在task中的变量 |
| isContinue(void):boolean | 是否成功断点续传 |
| getEtag(void):String | 获取当前下载获取到的ETag |
| getAutoRetryTimes(void):int | 自动重试次数 |
| getRetryingTimes(void):int | 当前重试次数。将要开始重试的时候,会将接下来是第几次 |
| isSyncCallback(void):boolean | 是否是设置了所有FileDownloadListener中的回调都直接在下载线程直接回调而不抛到ui线程 |
| getSpeed():int | 获取任务的下载速度, 下载过程中为实时速度,下载结束状态为平均速度 |
| isUsing():boolean | 判断当前的Task对象是否在引擎中启动过 |
| isWifiRequired():boolean | 获取当前任务是否被设置过只允许在Wifi网络环境下下载 |
FileDownloadListener)说明pending -> started -> connected -> (progress <->progress) -> blockComplete -> completed
paused / completed / error / warn
isReusedOldFile进行决策是否是该情况)(也可以通过setForceReDownload(true)来避免该情况):blockComplete -> completed
| 回调方法 | 备注 | 带回数据 |
|---|---|---|
| pending | 等待,已经进入下载队列 | 数据库中的soFarBytes与to |
$ claude mcp add FileDownloader \
-- python -m otcore.mcp_server <graph>