android中DownloadManager實現版本更新,監(jiān)聽下載進度實例
DownloadManager簡介
DownloadManager是Android 2.3(API level 9)用系統(tǒng)服務(Service)的方式提供了DownloadManager來處理長時間的下載操作。它包含兩個靜態(tài)內部類DownloadManager.Query(用來查詢下載信息)和DownloadManager.Request(用來請求一個下載)。
DownloadManager主要提供了下面幾個方法:
public long enqueue(Request request)把任務加入下載隊列并返回downloadId,以便后面用于查詢下載信息。若網絡不滿足條件、Sdcard掛載中、超過最大并發(fā)數等異常會等待下載,正常則直接下載。
public int remove(long… ids)刪除下載,若取消下載,會同時刪除下載文件和記錄。
public Cursor query(Query query)查詢下載信息,包括下載文件總大小,已經下載的大小以及下載狀態(tài)等。
ContentObserver簡介
public void ContentObserver(Handler handler) 所有ContentObserver的派生類都需要調用該構造方法,參數:handler Handler對象用于在主線程中修改UI。
public void onChange(boolean selfChange)當觀察到的Uri中內容發(fā)生變化時,就會回調該方法。所有ContentObserver的派生類都需要重載該方法去處理邏輯。
觀察特定Uri的步驟如下:
1、創(chuàng)建我們特定的ContentObserver派生類,必須重載父類構造方法,必須重載onChange()方法去處理回調后的功能實現。
2、為指定的Uri注冊一個ContentObserver派生類實例,當給定的Uri發(fā)生改變時,回調該實例對象去處理,調用registerContentObserver()方法去注冊內容觀察者。
3、由于ContentObserver的生命周期不同步于Activity和Service等。因此,在不需要時,需要手動的調用unregisterContentObserver()注銷內容觀察者。
效果圖:

一:執(zhí)行下載
下載配置
downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); downloadObserver = new DownloadChangeObserver(); //在執(zhí)行下載前注冊內容監(jiān)聽者 registerContentObserver(); DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); /**設置用于下載時的網絡狀態(tài)*/ request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE); /**設置通知欄是否可見*/ request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN); /**設置漫游狀態(tài)下是否可以下載*/ request.setAllowedOverRoaming(false); /**如果我們希望下載的文件可以被系統(tǒng)的Downloads應用掃描到并管理, 我們需要調用Request對象的setVisibleInDownloadsUi方法,傳遞參數true.*/ request.setVisibleInDownloadsUi(true); /**設置文件保存路徑*/ request.setDestinationInExternalFilesDir(getApplicationContext(), "phoenix", "phoenix.apk"); /**將下載請求放入隊列, return下載任務的ID*/ downloadId = downloadManager.enqueue(request); //執(zhí)行下載任務時注冊廣播監(jiān)聽下載成功狀態(tài) registerBroadcast();
添加權限
<!--網絡通信權限--> <uses-permission android:name="android.permission.INTERNET"/> <!--SD卡寫入數據權限--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!--SD卡創(chuàng)建與刪除權限--> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!--VISIBILITY_HIDDEN表示不顯示任何通知欄提示的權限--> <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/> <!--DownloadManager--> <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/> 在清單文件中注冊Service <!--版本更新服務--> <service android:name="com.github.phoenix.service.DownloadService"></service>
二:監(jiān)聽下載進度
注冊ContentObserver
三個參數分別是所要監(jiān)聽的Uri、false表示精確匹配此Uri,true表示可以匹配其派生的Uri、ContentObserver的派生類實例。
/**
* 注冊ContentObserver
*/
private void registerContentObserver() {
/** observer download change **/
if (downloadObserver != null) {
getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true, downloadObserver);
}
}
查詢已下載數據大小
為了提高性能,在這里開啟定時任務,每2秒去查詢數據大小并發(fā)送到handle中更新UI。
/**
* 監(jiān)聽下載進度
*/
private class DownloadChangeObserver extends ContentObserver {
public DownloadChangeObserver() {
super(downLoadHandler);
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
}
/**
* 當所監(jiān)聽的Uri發(fā)生改變時,就會回調此方法
*
* @param selfChange 此值意義不大, 一般情況下該回調值false
*/
@Override
public void onChange(boolean selfChange) {
scheduledExecutorService.scheduleAtFixedRate(progressRunnable, 0, 2, TimeUnit.SECONDS);
}
}
/**
* 通過query查詢下載狀態(tài),包括已下載數據大小,總大小,下載狀態(tài)
*
* @param downloadId
* @return
*/
private int[] getBytesAndStatus(long downloadId) {
int[] bytesAndStatus = new int[]{
-1, -1, 0
};
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor cursor = null;
try {
cursor = downloadManager.query(query);
if (cursor != null && cursor.moveToFirst()) {
//已經下載文件大小
bytesAndStatus[0] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
//下載文件的總大小
bytesAndStatus[1] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
//下載狀態(tài)
bytesAndStatus[2] = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return bytesAndStatus;
}
Activity與Service通信
既然我們要在Activity中實時更新下載進度,那么就需要Activity綁定Service建立通信。
在Service中提供一個接口實時回調進度值。用isBindService來標識Activity是否綁定過Service,在調用bindService(ServiceConnection conn)方法時,如果綁定成功會返回true,否則返回false,只有返回true時才可以進行解綁,否則報錯。
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
DownloadService.DownloadBinder binder = (DownloadService.DownloadBinder) service;
DownloadService downloadService = binder.getService();
//接口回調,下載進度
downloadService.setOnProgressListener(new DownloadService.OnProgressListener() {
@Override
public void onProgress(float fraction) {
LogUtil.i(TAG, "下載進度:" + fraction);
bnp.setProgress((int)(fraction * 100));
//判斷是否真的下載完成進行安裝了,以及是否注冊綁定過服務
if (fraction == DownloadService.UNBIND_SERVICE && isBindService) {
unbindService(conn);
isBindService = false;
MToast.shortToast("下載完成!");
}
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
三:廣播監(jiān)聽下載成功
下載完成,自動安裝,記錄APK存儲路徑
在下載成功后把APK存儲路徑保存到SP中,同時關閉定時器,開啟apk安裝界面。
/**
* 安裝APK
* @param context
* @param apkPath 安裝包的路徑
*/
public static void installApk(Context context, Uri apkPath) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
//此處因為上下文是Context,所以要加此Flag,不然會報錯
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(apkPath, "application/vnd.android.package-archive");
context.startActivity(intent);
}
四:善后處理
1、關閉定時器,線程
當收到下載完成的廣播時立即停掉定時器,取消線程。
2、解綁Service,注銷廣播,注銷ContentObserver
當Service解綁的時候,要把監(jiān)聽下載完成的廣播和監(jiān)聽下載進度的ContentObserver注銷。
3、刪除APK
當應用安裝成功后,再次啟動就執(zhí)行刪除Apk操作。
/**
* 刪除上次更新存儲在本地的apk
*/
private void removeOldApk() {
//獲取老APK的存儲路徑
File fileName = new File(SPUtil.getString(Constant.SP_DOWNLOAD_PATH, ""));
LogUtil.i(TAG, "老APK的存儲路徑 =" + SPUtil.getString(Constant.SP_DOWNLOAD_PATH, ""));
if (fileName != null && fileName.exists() && fileName.isFile()) {
fileName.delete();
LogUtil.i(TAG, "存儲器內存在老APK,進行刪除操作");
}
}
五:具體應用
首先上傳當前應用版本號給服務器,讓服務器檢查是否可以進行版本更新;如果可以進行版本更新,則綁定Service,開始下載APK,下載完成直接彈出安裝界面,同時記錄APK存儲路徑;待下次啟動時,檢查刪除APK。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
android studio 4.0 新建類沒有修飾符的方法
這篇文章主要介紹了android studio 4.0 新建類沒有修飾符的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10
使用Android Studio 開發(fā)自己的SDK教程
很多時候我們要將自己開發(fā)一個類庫打包成jar包以供他調用,這個jar包也叫你自己的SDK或者叫l(wèi)ibrary。android studio生成jar包的方法與eclipse有所不同。在studio中l(wèi)ibrary其實是module的概念。2017-10-10
基于Android引入IjkPlayer無法播放mkv格式視頻的解決方法
下面小編就為大家分享一篇基于Android引入IjkPlayer無法播放mkv格式視頻的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01

