標籤:
http://www.trinea.cn/android/android-downloadmanager/
本文主要結合源碼介紹Android系統下載管理DownloadManager的強大功能及使用。
這是許久來準備寫的一系列部落格,這篇主要介紹DownloadManager的功能和樣本,後面還有兩篇會介紹下載管理的底層設計(DownloadProvider、DownloadManager、DownloadManagerUI)、下載管理如何進行功能增強和bug修改。
樣本APK可從這些地址下載:Google Play, 360手機小幫手, 百度手機小幫手, 小米市集, 豌豆莢
可運行代碼地址可見DownloadManager Demo,如下:
關於下載管理的廢話
一、DownloadManager簡單介紹
DownloadManager是系統開放給第三方應用使用的類,包含兩個靜態內部類DownloadManager.Query和DownloadManager.Request。DownloadManager.Request用來請求一個下載,DownloadManager.Query用來查詢下載資訊,這兩個類的具體功能會在後面穿插介紹。DownloadManager的源碼可見[email protected]。
DownloadManager主要提供了下面幾個介面:
public long enqueue(Request request)執行下載,返回downloadId,downloadId可用於後面查詢下載資訊。若網路不滿足條件、Sdcard掛載中、超過最大並發數等異常會等待下載,正常則直接下載。
public int remove(long… ids)刪除下載,若下載中取消下載。會同時刪除下載檔案和記錄。
public Cursor query(Query query)查詢下載資訊。
public static Long getRecommendedMaxBytesOverMobile(Context context通過移動網路下載的最大位元組數
public String getMimeTypeForDownloadedFile(long id)得到下載的mimeType,如何設定後面會進行介紹
其它:通過查看代碼我們可以發現還有個CursorTranslator私人靜態內部類。這個類主要對Query做了一層代理。將DownloadProvider和DownloadManager之間做個映射。將DownloadProvider中的十幾種狀態對應到了DownloadManager中的五種狀態,DownloadProvider中的失敗、暫停原因轉換為了DownloadManager的原因。
二、下載管理樣本
下面具體介紹利用DownloadManager進行下載。
1、AndroidManifest中添加許可權
Java
| 12 |
<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
網路存取權限是必須的,為sdcard的話需要添加sdcard寫入權限。
2、調用DownloadManager.Request開始下載
Java
| 1234567891011 |
DownloadManager downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);String apkUrl = "http://img.meilishuo.net/css/images/AndroidShare/Meilishuo_3.6.1_10006.apk";DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));request.setDestinationInExternalPublicDir("Trinea", "MeiLiShuo.apk");// request.setTitle("MeiLiShuo");// request.setDescription("MeiLiShuo desc");// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);// request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);// request.setMimeType("application/cn.trinea.download.file");long downloadId = downloadManager.enqueue(request); |
上面調用downloadManager的enqueue介面進行下載,返回唯一的downloadId。
DownloadManager.Request除了建構函式的Uri必須外,其他設定都為可選設定。下面逐個介紹下:
request.setDestinationInExternalPublicDir(“Trinea”, “MeiLiShuo.apk”);表示設定為sd卡的Trinea檔案夾,檔案名稱為MeiLiShuo.apk。
setDestinationInExternalPublicDir源碼
從源碼中我們可以看出下載完整目錄為Environment.getExternalStoragePublicDirectory(dirType)。不過file是通過file.mkdir()建立的,這樣如果上級目錄不存在就會建立檔案夾異常。所以下載前我們最好自己調用File的mkdirs方法遞迴建立子目錄,如下:
Java
| 12 |
File folder = new File(folderName);return (folder.exists() && folder.isDirectory()) ? true : folder.mkdirs(); |
否則,會報異常
| 12 |
java.lang.IllegalStateException: Unable to create directory: /storage/sdcard0/Trinea/aaat android.app.DownloadManager$Request.setDestinationInExternalPublicDir(DownloadManager.java) |
其他設定下載路徑介面為setDestinationUri,setDestinationInExternalFilesDir,setDestinationToSystemCache。其中setDestinationToSystemCache僅限系統app使用。
request.allowScanningByMediaScanner();表示允許MediaScanner掃描到這個檔案,預設不允許。
request.setTitle(“MeiLiShuo”);設定下載中通知欄提示的標題
request.setDescription(“MeiLiShuo desc”);設定下載中通知欄提示的介紹
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
表示下載進行中和下載完成的通知欄是否顯示。預設只顯示下載中通知。VISIBILITY_VISIBLE_NOTIFY_COMPLETED表示下載完成後顯示通知欄提示。VISIBILITY_HIDDEN表示不顯示任何通知欄提示,這個需要在AndroidMainfest中添加許可權android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
表示下載允許的網路類型,預設在任何網路下都允許下載。有NETWORK_MOBILE、NETWORK_WIFI、NETWORK_BLUETOOTH三種及其組合可供選擇。如果只允許wifi下載,而當前網路為3g,則下載會等待。
request.setAllowedOverRoaming(boolean allow)移動網路情況下是否允許漫遊。
request.setMimeType(“application/cn.trinea.download.file”);
設定下載檔案的mineType。因為下載管理Ui中點擊某個已下載完成檔案及下載完成點擊通知欄提示都會根據mimeType去開啟檔案,所以我們可以利用這個屬性。比如上面設定了mimeType為application/cn.trinea.download.file,我們可以同時設定某個Activity的intent-filter為application/cn.trinea.download.file,用於響應點擊的開啟檔案。
Java
| 1234567 |
<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/cn.trinea.download.file" /></intent-filter> |
request.addRequestHeader(String header, String value)
添加請求下載的網路連結的http頭,比如User-Agent,gzip壓縮等
3 下載進度狀態監聽及查詢
下載進度狀態監聽代碼
其中我們會監聽Uri.parse(“content://downloads/my_downloads”)。然後查詢下載狀態和進度,發送handler進行更新,handler中處理就是設定進度條和狀態等。
其中DownloadManagerPro.getBytesAndStatus的主要代碼如下,可直接引入[email protected](歡迎star和fork^_^)作為你項目的library(如何拉取代碼及添加公用庫):
下載進度狀態查詢代碼
從上面代碼可以看出我們主要調用DownloadManager.Query()進行查詢。DownloadManager.Query為下載管理對外開放的資訊查詢類,主要包括以下介面:
setFilterById(long… ids)根據下載id進行過濾
setFilterByStatus(int flags)根據下載狀態進行過濾
setOnlyIncludeVisibleInDownloadsUi(boolean value)根據是否在download ui中可見進行過濾。
orderBy(String column, int direction)根據列進行排序,不過目前僅支援DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP和DownloadManager.COLUMN_TOTAL_SIZE_BYTES排序。
4 下載成功監聽
下載完成後,下載管理會發出DownloadManager.ACTION_DOWNLOAD_COMPLETE這個廣播,並傳遞downloadId作為參數。通過接受廣播我們可以開啟對下載完成的內容進行操作。代碼如下:
下載成功監聽
5、響應通知欄點擊
(1) 響應下載中通知欄點擊
點擊下載中通知欄提示,系統會對下載的應用單獨發送Action為DownloadManager.ACTION_NOTIFICATION_CLICKED廣播。intent.getData為content://downloads/all_downloads/29669,最後一位為downloadId。
如果同時下載多個應用,intent會包含DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS這個key,表示下載的的downloadId數組。這裡設計到下載管理通知欄的顯示機制,會在下一篇具體介紹。
(2) 響應下載完成通知欄點擊
下載完後會調用下面代碼進行處理,從中我們可以發現系統會調用View action根據mimeType去查詢。所以可以利用我們在介紹的DownloadManager.Request的setMimeType函數。
openDownload源碼
如果介面上過多元素需要更新,且網速較快不斷的執行onChange會對頁面效能有一定影響。推薦ScheduledExecutorService定期查詢,如下:
Java
| 123456789 |
public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);Runnable command = new Runnable() { @Override public void run() { updateView(); } };scheduledExecutorService.scheduleAtFixedRate(command, 0, 3, TimeUnit.SECONDS); |
表示3秒定時重新整理
Android系統下載管理DownloadManager功能介紹及使用樣本