关于FileDownLoader这个下载引擎在这里我就不多做介绍了,具体可以打开github上的地址:FileDownLoader 查看~
博客结尾处也有简略说明
使用FileDownLoader
第一步:如果使用的是AndroidStudio开发工具,需要在项目中引用:
compile 'com.liulishuo.filedownloader:library:1.3.0' 第二步:然后在Application.Oncreate 中进行全局初始化 public XXApplication extends Application{ ... @Override public void onCreate() { /** * 仅仅是缓存Application的Context,不耗时 */ FileDownloader.init(getApplicationContext); } ... }
下载介绍一下我写的单例下载:
1.单任务下载
<span style="font-size:14px;">package com.kuaibao.skuaidi.manager; import com.liulishuo.filedownloader.BaseDownloadTask; import com.liulishuo.filedownloader.FileDownloadListener; import com.liulishuo.filedownloader.FileDownloader; /** * Created by gudd * on 2016/10/27. * compile 'com.liulishuo.filedownloader:library:1.3.0' */ public class FileDownloaderManager { public static FileDownloaderManager instance = null; private FileDownloaderManager() { }// 避免类在外部被实例化 public static FileDownloaderManager getInstance() { if (null == instance) { instance = new FileDownloaderManager(); } return instance; } /** * 单任务下载 * * @param downLoadUri 文件下载网络地址 * @param destinationUri 下载文件的存储绝对路径 */ public void startDownLoadFileSingle(String downLoadUri, String destinationUri,FileDownLoaderCallBack callBack) { FileDownloader.getImpl().create(downLoadUri).setPath(destinationUri).setListener(fileDownloadListener(callBack)).start(); } // 下载方法 private FileDownloadListener fileDownloadListener(final FileDownLoaderCallBack callBack) { return new FileDownloadListener() { @Override protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) { //等待,已经进入下载队列 } @Override protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) { //下载进度回调 if (callBack != null){ callBack.downLoadProgress(task,soFarBytes,totalBytes); } } @Override protected void completed(BaseDownloadTask task) { //完成整个下载过程 if (callBack != null){ callBack.downLoadComplated(task); } } @Override protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) { //暂停下载 } @Override protected void error(BaseDownloadTask task, Throwable e) { //下载出现错误 if (callBack != null){ callBack.downLoadError(task,e); } } @Override protected void warn(BaseDownloadTask task) { //在下载队列中(正在等待/正在下载)已经存在相同下载连接与相同存储路径的任务 } }; } // 这里是下载过程中的回调,由于下载过程中,我们只需要知道文件是否下载完成、文件是否下载失败、还有文件下载进度,</span>
所以这里只自定义三个方法用于回调
<span style="font-size:14px;"> public interface FileDownLoaderCallBack{ void downLoadComplated(BaseDownloadTask task); void downLoadError(BaseDownloadTask task, Throwable e); void downLoadProgress(BaseDownloadTask task, int soFarBytes, int totalBytes); } }</span>
所以,你在使用的时候只需要使用一行代码就可以下载文件了:
[java] view plain copy FileDownloaderManager.getInstance().startDownLoadFileSingle("下载地址",“保存地址”,new FileDownLoaderCallBack({ ...... }));
所有的暂停,就是停止,会释放所有资源并且停到所有相关线程,下次启动的时候默认会断点续传
方法名备注init(Context)缓存Context,不会启动下载进程init(Context, InitCustomMaker)缓存Context,不会启动下载进程;在下载进程启动的时候,会传入定制化组件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的soFarBytesgetTotal(downloadId)获得下载Id为downloadId的totalBytesbindService(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个回调。默认值: 5enableAvoidDropFrame(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数据库中的所有数据
你有两种方法可以解决这个问题
FileDownloader#enableAvoidDropFrame, 默认 就是开启的BaseDownloadTask#setSyncCallback, 默认是false, 如果设置为true,所有的回调都会在下载线程直接同步调用而不会抛到ui线程。你可以添加一个全局监听器来进行打点或者是调试
方法名备注setGlobalMonitor(monitor:IMonitor)设置与替换一个全局监听器到下载引擎中releaseGlobalMonitor(void)释放已经设置到下载引擎中的全局监听器getMonitor(void)获取已经设置到下载引擎中的全局监听器监听器接口类
接口备注onRequestStart(count:int, serial:boolean, lis:FileDownloadListener)将会在启动队列任务是回调这个方法onRequestStart(task:BaseDownloadTask)将会在启动单一任务时回调这个方法onTaskBegin(task:BaseDownloadTask)将会在内部接收并开始task的时候回调这个方法(会在pending回调之前)onTaskStarted(task:BaseDownloadTask)将会在task结束pending开始task的runnable的时候回调该方法onTaskOver(task:BaseDownloadTask)将会在task走完所有生命周期是回调这个方法如何快速集成Notification呢? 建议参考NotificationMinSetActivity、NotificationSampleActivity。
如果你需要定制化FileDownloader,可以在你的项目模块的assets 目录下添加 'filedownloader.properties' 文件(如/demo/src/main/assets/filedownloader.properties),然后添加以下可选相关配置。
格式: keyword=value
关键字描述默认值http.lenient如果你遇到了: 'can't know the size of the download file, and its Transfer-Encoding is not Chunked either', 但是你想要忽略类似的返回头不规范的错误,直接将该关键字参数设置为true即可,我们将会将其作为chunck进行处理falseprocess.non-separateFileDownloadService 默认是运行在独立进程':filedownloader'上的, 如果你想要FileDownloadService共享并运行在主进程上, 将该关键字参数设置为true,可以有效减少IPC产生的I/Ofalsedownload.min-progress-step最小缓冲大小,用于判定是否是时候将缓冲区中进度同步到数据库,以及是否是时候要确保下缓存区的数据都已经写文件。值越小,更新会越频繁,下载速度会越慢,但是应对进程被无法预料的情况杀死时会更加安全65536download.min-progress-time最小缓冲时间,用于判定是否是时候将缓冲区中进度同步到数据库,以及是否是时候要确保下缓存区的数据都已经写文件。值越小,更新会越频繁,下载速度会越慢,但是应对进程被无法预料的情况杀死时会更加安全2000download.max-network-thread-count用于同时下载的最大网络线程数, 区间[1, 12]3file.non-pre-allocation是否不需要在开始下载的时候,预申请整个文件的大小(content-length)falseIII. 异常处理
所有的异常,都将在 FileDownloadListener#error(BaseDownloadTask, Throwable) 中获知。
Exception原因FileDownloadHttpException在发出请求以后,response-code不是200(HTTP_OK),也不是206(HTTP_PARTIAL)的情况下会抛出该异常; 在这个异常对象会带上 response-code、response-header、request-header。FileDownloadGiveUpRetryException在请求返回的 response-header 中没有带有文件大小(content-length),并且不是流媒体(transfer-encoding)的情况下会抛出该异常;出现这个异常,将会忽略所有重试的机会(BaseDownloadTask#setAutoRetryTimes). 你可以通过在filedownloader.properties中添加 http.lenient=true 来忽略这个异常,并且在该情况下,直接作为流媒体进行下载。FileDownloadOutOfSpaceException当将要下载的文件大小大于剩余磁盘大小时,会抛出这个异常。其他程序错误。这边的数据并不多,只是一些队列数据,用不了多少内存。
如果在前台的时候这个数据都被回收了, 你的应用应该也挂了。极低概率事件。
一般事件, 如果是你的下载是UI进程启动的,如果你的UI进程处于后台进程(可以理解为应用被退到后台)状态,在内存不足的情况下会被回收(回收优先级高于服务进程),此时分两种情况:
是串行队列任务,在回收掉UI进程内存以后,下载进程会继续下载完已经pending到下载进程的那个任务,而还未pending到下载进程的任务会中断下载(由于任务驱动线性执行的是在UI进程); 有损体验: 下次进入应用重启启动整个队列,会继续上次的下载。
是并行队列任务,在回收掉UI进程内存以后,下载进程会继续下载所有任务(所有已经pending到下载进程的任务,由于这里的pending速度是很快的,因此几乎是点击并行下载,所有任务在很短的时间内都已经pending到下载进程了),而UI进程由于被回收,将不会收到所有的监听; 有损体验: 下次进入应用重新启动整个队列,就会和正常的下载启动一致,收到所有情况的监听。
对内存有一定的占用,但是并不多,每次启动进程会根据数据的有效性进行清理冗余数据,被回收是低概率事件
由于下载不断有不同的buffer占用内存,但是由于在下载时,是活跃的服务进程,因此被回收是低概率事件(会先回收完所有空进程、后台进程(后台应用)以后,如果内存还不够,才会回收该进程)。
即使被回收,也不会有任何问题。由于我们使用的是START_STICKY(如果不希望被重启可主动调用FileDownloader#unBindService/FileDownloader#unBindServiceIfIdle),因此在内存足够的时候,下载进程会尝试重启(系统调度),非下载进程(一般是UI进程) 接收到下载进程的连接,会继续下载与继续接收回调,下载进程也会断点续传没有下载完的所有任务(无论并行与串行),不会影响体验。