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 ;
    }

}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM