android 开发 框架系列 使用 FileDownloader 实现检查更新的功能class
首先介绍一下FileDownloader
GH :https://github.com/lingochamp/FileDownloader/blob/master/README-zh.md
FileDownloader2
现在, FileDownloader2-OkDownload 已经正式发布, okdownload继承了所有FileDownloader的优点,甚至做了更多的优化以及更多的特性。
特点
- 简单易用
- 单任务多线程/多连接/分块下载(并支持通过
ConnectionCountAdapter
定制) - 高并发
- 灵活
- 可选择性支持: 独立/非独立进程
- 自动断点续传
需要注意
- 当下载的文件大小可能大于1.99GB(2^31-1
=2_147_483_647 = 1.99GB
)的时候, 请使用FileDownloadLargeFileListener
而不是FileDownloadListener
(同理使用getLargeFileSofarBytes()
与getLargeFileTotalBytes()
) - 暂停: paused, 恢复: 直接调用start,默认就是断点续传
- 引擎默认会打开避免掉帧的处理(使得在有些情况下回调(FileDownloadListener)不至于太频繁导致ui线程被ddos), 如果你希望关闭这个功能(关闭以后,所有回调会与0.1.9之前的版本一样,所有的回调会立马抛一个消息ui线程(Handler))
- 如果没有特殊需要,直接通过配置
filedownloader.properties
将process.non-separate
置为true
,可以有效减少每次回调IPC带来的I/O。
I. 效果
II. 使用
在项目中引用:
implementation 'com.liulishuo.filedownloader:library:1.7.5'
如果是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即可。
Task接口说明
方法名 | 备注 |
---|---|
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与totalBytes |
started | 结束了pending,并且开始当前任务的Runnable | - |
connected | 已经连接上 | ETag, 是否断点续传, soFarBytes, totalBytes |
progress | 下载进度回调 | soFarBytes |
blockComplete | 在完成前同步调用该方法,此时已经下载完成 | - |
retry | 重试之前把将要重试是第几次回调回来 | 之所以重试遇到Throwable, 将要重试是第几次, soFarBytes |
completed | 完成整个下载过程 | - |
paused | 暂停下载 | soFarBytes |
error | 下载出现错误 | 抛出的Throwable |
warn | 在下载队列中(正在等待/正在下载)已经存在相同下载连接与相同存储路径的任务 | - |
由于FileDownloadListener
中的方法回调过快,导致掉帧?
你有两种方法可以解决这个问题
FileDownloader#enableAvoidDropFrame
, 默认 就是开启的BaseDownloadTask#setSyncCallback
, 默认是false, 如果设置为true,所有的回调都会在下载线程直接同步调用而不会抛到ui线程。
FileDownloadMonitor
你可以添加一个全局监听器来进行打点或者是调试
方法名 | 备注 |
---|---|
setGlobalMonitor(monitor:IMonitor) | 设置与替换一个全局监听器到下载引擎中 |
releaseGlobalMonitor(void) | 释放已经设置到下载引擎中的全局监听器 |
getMonitor(void) | 获取已经设置到下载引擎中的全局监听器 |
FileDownloadMonitor.IMonitor
监听器接口类
接口 | 备注 |
---|---|
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走完所有生命周期是回调这个方法 |
FileDownloadUtils
方法名 | 备注 |
---|---|
setDefaultSaveRootPath(path:String) | 在整个引擎中没有设置路径时BaseDownloadTask#setPath 这个路径将会作为它的Root path |
getTempPath | 获取用于存储还未下载完成文件的临时存储路径: filename.temp |
isFilenameConverted(context:Context) | 判断是否所有数据库中下载中的任务的文件名都已经从filename (在旧架构中)转为filename.temp |
FileDownloadNotificationHelper
如何快速集成Notification呢? 建议参考NotificationMinSetActivity、NotificationSampleActivity。
filedownloader.properties
如果你需要定制化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 进行处理 |
false |
process.non-separate | FileDownloadService 默认是运行在独立进程':filedownloader'上的, 如果你想要FileDownloadService共享并运行在主进程上, 将该关键字参数设置为true ,可以有效减少IPC产生的I/O |
false |
download.min-progress-step | 最小缓冲大小,用于判定是否是时候将缓冲区中进度同步到数据库,以及是否是时候要确保下缓存区的数据都已经写文件。值越小,更新会越频繁,下载速度会越慢,但是应对进程被无法预料的情况杀死时会更加安全 | 65536 |
download.min-progress-time | 最小缓冲时间,用于判定是否是时候将缓冲区中进度同步到数据库,以及是否是时候要确保下缓存区的数据都已经写文件。值越小,更新会越频繁,下载速度会越慢,但是应对进程被无法预料的情况杀死时会更加安全 | 2000 |
download.max-network-thread-count | 用于同时下载的最大网络线程数, 区间[1, 12] | 3 |
file.non-pre-allocation | 是否不需要在开始下载的时候,预申请整个文件的大小(content-length ) |
false |
broadcast.completed | 是否需要在任务下载完成后发送一个完成的广播 | false |
如果你使用
broadcast.completed
并且接收任务完成的广播,你需要注册Action为filedownloader.intent.action.completed
的广播并且使用FileDownloadBroadcastHandler
来处理接收到的Intent
。
III. 异常处理
所有的异常,都将在
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 |
当将要下载的文件大小大于剩余磁盘大小时,会抛出这个异常。 |
其他 | 程序错误。 |
FileDownloadNetworkPolicyException |
设置了BaseDownloadTask#setWifiRequired(true) ,在下载过程中,一旦发现网络情况转为非Wifi环境,便会抛回这个异常 |
PathConflictException |
当有一个正在下载的任务,它的存储路径与当前任务的存储路径完全一致,为了避免多个任务对同一个文件进行写入,当前任务便会抛回这个异常 |
个人代码,检查更新app class
package net.yt.yuncare.widgets; import android.app.DownloadManager; import android.content.Context;
import android.content.Intent; import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.content.FileProvider;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.liulishuo.filedownloader.BaseDownloadTask;
import com.liulishuo.filedownloader.FileDownloadListener;
import com.liulishuo.filedownloader.FileDownloader; import java.io.File;
/*
content:检查版本、更新下载功能class
time:2018-7-23 11:23
*/ public class CheckUpdate {
private final String TAG= "CheckUpdate_检查更新";
private Context mContext;
private DownloadManager mDownloadManager;
private MaterialDialog mDialog;
private DownloadManager.Request mRequest;
private int mDownId;
private Handler mHandler;
private String mDownPath;
private String mDownApkName; public CheckUpdate(Context context){
this.mContext = context; } /*
* 对比apk版本号的方法
* @param float 要对比的目标版本号
* @return true=版本号一致,false=版本号不一致
*/
public boolean CheckVersion(float targetVersion){
String packagename = mContext.getPackageName();
float version = 0;
try {
//获取当前apk的版本号, getPackageInfo = 包信息 :参数为(包名,版本号曾量)
version = (float) mContext.getPackageManager().getPackageInfo(packagename,0).versionCode;
Log.e(TAG, "pkgVersion:"+false); } catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} if (version == targetVersion){
return true;
}else {
return false;
}
} /*
* 下载apk的方法
* @param String downUri : 参数是下载apk的网络地址Uri
* @param String downPath : 参数是存放下载内容的文件目录路径
* @param String downApkName : 参数是设置下载完成后的apk名称。注意!名称后面要加.apk后缀名
* @param DisplayMode displayMode : 下载模式,推荐使用 DisplayMode.MODE_DIALOG_BOX ,系统的未完善
*
*/ public void downNewApk(String downUri,String downPath,String downApkName,DisplayMode displayMode){ if(mContext == null){
Log.e(TAG, "Error:mContext is null");
return;
}
if (downUri == null){
Log.e(TAG, "Error:downUri is null");
return;
}
if (downPath.equals("")&&downPath==null){
Log.e(TAG, "Error:downPath is null");
return;
}
if (downApkName.equals("")&& downApkName == null){
Log.e(TAG, "Error:downApkName is null");
}
mDownPath = downPath;
mDownApkName = downApkName; if(displayMode == DisplayMode.MODE_DIALOG_BOX){ //使用第三方下载 dialog(downUri); }else if (displayMode == DisplayMode.MODE_STATUS_BAR){
//使用系统下载,注意!此系统下载没有写下载完成的广播监听,所以无法自动安装apk
mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
//初始化下载的request
mRequest = new DownloadManager.Request(Uri.parse(downUri));
//设置允许网络类型 分别是 移动网络 和 WiFi网络
mRequest.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
mRequest.setAllowedOverRoaming(false);
//设置文件类型
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(downUri));
mRequest.setMimeType(mimeString);
Log.e(TAG, "选择模式:状态栏");
mRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);//通知可见性 ,参数为可见度为可见
mRequest.setShowRunningNotification(true); //设置显示运行通知
mRequest.setVisibleInDownloadsUi(true); //在下载中设置UI可见的
//设置下载保存路径
mRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, downApkName);
mRequest.setTitle(downApkName);
//插入下载队列.返回下载对象Id
mDownId = (int)mDownloadManager.enqueue(mRequest);
Toast.makeText(mContext,"正在更新",Toast.LENGTH_SHORT).show(); }else {
Log.e(TAG, "Error:downNewApk @param is null");
return;
}
} private void dialog (String url){
File file = new File(mDownPath);
File fileApk = new File(mDownPath+mDownApkName);
if (fileApk.exists()){
fileApk.delete();
}
if (!file.exists()){
file.mkdirs();
}
mDialog = new MaterialDialog.Builder(mContext)
.title("更新中")
.canceledOnTouchOutside(false)
.progress(false,100,false)
.positiveText("取消更新")
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
FileDownloader.getImpl().pauseAll();//暂停所有任务
FileDownloader.getImpl().clear(mDownId,mDownPath+"/"+mDownApkName);//强制清除下载的临时文件
FileDownloader.getImpl().clearAllTaskData();//清空下载任务数据库
FileDownloader.getImpl().unBindService(); //关闭下载进程
dialog.dismiss(); }
})
.build(); mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0x0001:
int soFarBytes = msg.getData().getInt("soFarBytes",0);
int totalBytes = msg.getData().getInt("totalBytes",0);
float progress = ((float)soFarBytes/(float)totalBytes)*100;
Log.e(TAG, "pending:接受更新值: soFarBytes="+soFarBytes+" totalBytes="+totalBytes+" progress="+progress);
mDialog.setProgress((int)progress);
mDialog.setContent(soFarBytes+"/"+totalBytes+" KB");
break;
case 0x0002:
mDialog.dismiss();
break;
case 0x0003:
Log.e(TAG, "mHandler:显示对话框");
mDialog.show();
break;
default:
break; }
}
}; FileDownloader.setup(mContext);
FileDownloader.getImpl()
.create(url)
.setPath(mDownPath+"/"+mDownApkName)
.setListener(new FileDownloadListener() {
@Override //等待 参数 task=下载任务 soFarBytes=当前已经下载字节 totalBytes=总字节
protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) {
mDownId = task.getId();
Log.e(TAG, "pending:显示对话框");
Message message = Message.obtain();
message.what = 0x0003;
mHandler.sendMessage(message); } @Override //增量
protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
Bundle bundle = new Bundle();
Log.e(TAG, "progress:发生更新值");
bundle.putInt("soFarBytes",soFarBytes);
bundle.putInt("totalBytes",totalBytes);
Message message = Message.obtain();
message.setData(bundle);
message.what = 0x0001;
mHandler.sendMessage(message);
} @Override //全部下载完成后
protected void completed(BaseDownloadTask task) {
Log.e(TAG, "completed:下载完成");
Message message = Message.obtain();
message.what = 0x0002;
mHandler.sendMessage(message);
installApk(mDownPath+"/"+mDownApkName); } @Override //暂停
protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) { } @Override
protected void error(BaseDownloadTask task, Throwable e) {
Log.e(TAG, "FileDownloader error: "+e); } @Override
protected void warn(BaseDownloadTask task) { }
}).start(); } private void installApk(String path) {
Intent intent = new Intent(Intent.ACTION_VIEW);
File apkFile = new File(path); if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) { Uri contentUri = FileProvider.getUriForFile(mContext,mContext.getPackageName()+".provider",apkFile);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(contentUri,
"application/vnd.android.package-archive");
}else{ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive"); }
mContext.startActivity(intent);
} public enum DisplayMode {
MODE_DIALOG_BOX,MODE_STATUS_BAR ;
} }
android 开发 框架系列 使用 FileDownloader 实现检查更新的功能class的更多相关文章
- Android 开发 框架系列 OkHttp拦截器
前言 此篇博客只讲解okhttp的拦截器功能的详细使用,如果你还不太了解okhttp可以参考我另外一篇博客 Android 开发 框架系列 OkHttp使用详解 添加Interceptor的简单例子 ...
- Android 开发 框架系列 Google的ORM框架 Room
目录 简介 导入工程 使用流程概况 一个简单的小Demo 深入学习 @Entity使用 自定义表名 tableName 自定义字段名@ColumnInfo 主键 @PrimaryKey 索引 @In ...
- Android 开发 框架系列 OkHttp使用详解
简介 okhttp是一个第三方类库,用于android中请求网络.这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso和LeakCanary) . ...
- Android 开发 框架系列 百度语音合成
官方文档:http://ai.baidu.com/docs#/TTS-Android-SDK/6d5d6899 官方百度语音合成控制台:https://cloud.baidu.com/product/ ...
- Android 开发 框架系列 EventBus 事件总线
介绍 GitHub:https://github.com/greenrobot/EventBus 先聊聊EventBus 线程总线是干什么的,使用环境,优点.缺点. 干什么的? 一句话,简单统一数据传 ...
- Android 开发 框架系列 OkHttp文件上传功能实现(含断点续传)
前言 此篇博客只是上传功能的记录demo,如果你还不太了解okhttp可以参考我的另一篇博客https://www.cnblogs.com/guanxinjing/p/9708575.html 代码部 ...
- Android 开发 框架系列 OkHttp文件下载功能实现(含断点续传)
前言 此篇博客只是下载功能的记录demo,如果你还不太了解okhttp可以参考我的另一篇博客https://www.cnblogs.com/guanxinjing/p/9708575.html 代码部 ...
- Android 开发 框架系列 glide-transformations 图片处理基本使用
首先简单的介绍一下Gilde作用范围.Gilde功能十分强大,它可以实现图片处理.图片本地加载.图片网络加载.位图加载.图片内存缓存.图片磁盘缓存.Gif图片加载.使用简单轻松,轻松的后是它强大的心, ...
- Android 开发 框架系列 Android-Universal-Image-Loader 图片加载使用demo
Android-Universal-Image-Loader github地址:https://github.com/nostra13/Android-Universal-Image-Loader 加 ...
随机推荐
- 关于第一次在IED(MyEclipse)部署Maven时,pom.xml头部报这样的错---org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache.maven.archiver.MavenArchiveConfiguration)
rg.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache ...
- Ubuntu 17.10 安装Caffe(cpu)并配置Matlab接口
(1)安装依赖: sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-ser ...
- [BUG]读配置文件中文, 查询不到数据库
配置文件编码, 要和数据库编码一致
- centos7.5 安装mysql8.0.13
在Linux系统上使用rpm包管理器安装mysql Installing MySQL on Linux Using RPM Packages 环境:CentOS Linux release 7.4.1 ...
- iOS tableView分割线高度自定义
1.系统自带的集中分割线的形式 myTableView.separatorStyle=UITableViewCellSeparatorStyleNone;(这个是去掉所有分割线)可以通过这个来设置 2 ...
- 源代码安装-非ROOT用户安装软件的方法
0. 前言 如果你没有sudo权限,则很多程序是无法使用别人编译好的文件安装的. 还有时候,没有对应你的主机配置的安装包,这时候需要我们自己下载最原始的源代码,然后进行编译安装. 这样安装的程序 ...
- Vagrant 创建虚拟环境
1. 添加box vagrant box add --name centos6.8/cms boxname.box 2.初始化admin环境 vagrant init centos6.8/cms ad ...
- Android版本28使用http请求报错not permitted by network security policy
Android版本28使用http请求报错not permitted by network security policy android模拟器调试登录的时候报错 CLEARTEXT communic ...
- Java+opencv实现人脸检测
版本 Java1.8 opencv3.4 代码: import java.awt.Graphics; import java.awt.image.BufferedImage; import javax ...
- 学习笔记TF029:实现进阶卷积网络
经典数据集CIFAR-10,60000张32x32彩色图像,训练集50000张,测试集10000张.标注10类,每类图片6000张.airplance.automobile.bird.cat.deer ...