• 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.propertiesprocess.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) 如果pathAsDirectorytrue,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-MatchRange参数),请勿重复添加导致400或其他错误
    addHeader(line:String) 添加自定义的请求头参数,需要注意的是内部为了断点续传,在判断断点续传有效时会自动添加上(If-MatchRange参数),请勿重复添加导致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中的方法回调过快,导致掉帧?

    你有两种方法可以解决这个问题

    1. FileDownloader#enableAvoidDropFrame, 默认 就是开启的
    2. 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呢? 建议参考NotificationMinSetActivityNotificationSampleActivity

    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 ;
        }
    
    }
     
  • 相关阅读:
    myeclipse8.6注册码
    hibernate用注解(annotation)配置sequence
    MyEclipse注释配置
    JAVA 单选样式编写
    .Net中的几种异步模式
    C#并发编程——并发编程技术
    《CLR via C#》之线程处理——任务调度器
    《CLR via C#》之线程处理——线程池与任务
    《CLR via C#》之线程处理——协作式取消和超时
    《CLR via C#》之线程处理——线程基础
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/9708587.html
Copyright © 2020-2023  润新知