從Android 2.3(API level 9)開始Android用系統服務(Service)的方式提供了Download Manager來優化處理長時間的下載操作。Download Manager處理HTTP
連接并監控連接中的狀態變化以及系統重啟來確保每一個下載任務順利完成。
在大多數涉及到下載的情況中使用Download Manager都是不錯的選擇,特別是當用戶切換不同的應用以后下載需要在后臺繼續進行,以及當下載任務順利完成非常重要的情況(DownloadManager對于斷點續傳功能支持很好)。
一、DownloadManager簡單介紹
DownloadManager是系統開放給第三方應用使用的類,包含兩個靜態內部類DownloadManager.Query和DownloadManager.Request。DownloadManager.Request用來請求一個下載,DownloadManager.Query用來查詢下載信息,這兩個類的具體功能會在后面穿插介紹。DownloadManager的源碼可見DownloadManager@Grepcode。
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的原因。
簡單使用
完成一個下載任務只需要4行代碼,什么斷點續傳,大文件下載,通知欄進度顯示....都不需要你操心。
//創建下載任務,downloadUrl就是下載鏈接
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
//指定下載路徑和下載文件名
request.setDestinationInExternalPublicDir("/download/", fileName);
//獲取下載管理器
DownloadManager downloadManager= (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
//將下載任務加入下載隊列,否則不會進行下載
downloadManager.enqueue(request);
高級用法
具體的使用方法
要想使用Download Manager,使用getSystemService方法請求系統的DOWNLOAD_SERVICE服務,代碼片段如下
//創建下載任務 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(versionUrl)); request.setAllowedOverRoaming(false);//漫游網絡是否可以下載 //設置文件類型,可以在下載結束后自動打開該文件 MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(versionUrl)); request.setMimeType(mimeString); //在通知欄中顯示,默認就是顯示的 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); request.setVisibleInDownloadsUi(true); //sdcard的目錄下的download文件夾,必須設置 request.setDestinationInExternalPublicDir("/download/", versionName); //request.setDestinationInExternalFilesDir(),也可以自己制定下載路徑 //將下載請求加入下載隊列 downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); //加入下載隊列后會給該任務返回一個long型的id, //通過該id可以取消任務,重啟任務等等,看上面源碼中框起來的方法 mTaskId = downloadManager.enqueue(request);
要請求一個下載操作,需要創建一個DownloadManager.Request對象,將要請求下載的文件的Uri傳遞給Download Manager的enqueue方法
String serviceString = Context.DOWNLOAD_SERVICE;
DownloadManager downloadManager;
downloadManager = (DownloadManager)getSystemService(serviceString);
Uri uri = Uri.parse("http://developer.android.com/shareables/icon_templates-v4.0.zip");
DownloadManager.Request request = new Request(uri);
long reference = downloadManager.enqueue(request);
String serviceString = Context.DOWNLOAD_SERVICE;
DownloadManager downloadManager;
downloadManager = (DownloadManager)getSystemService(serviceString);
Uri uri = Uri.parse("http://developer.android.com/shareables/icon_templates-v4.0.zip");
DownloadManager.Request request = new Request(uri);
long reference = downloadManager.enqueue(request);
在這里返回的reference變量是系統為當前的下載請求分配的一個唯一的ID,我們可以通過這個ID重新獲得這個下載任務,進行一些自己想要進行的操作或者查詢
下載的狀態以及取消下載等等。
我們可以通過addRequestHeader方法為DownloadManager.Request對象request添加HTTP頭,也可以通過setMimeType方法重寫從服務器返回的mime type。
我們還可以指定在什么連接狀態下執行下載操作。setAllowedNetworkTypes方法可以用來限定在WiFi還是手機網絡下進行下載,setAllowedOverRoaming方法
可以用來阻止手機在漫游狀態下下載。
下面的代碼片段用于指定一個較大的文件只能在WiFi下進行下載:
request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
Android API level 11 介紹了getRecommendedMaxBytesOverMobile類方法(靜態方法),返回一個當前手機網絡連接下的最大建議字節數,可以來判斷下載
是否應該限定在WiFi條件下。
調用enqueue方法之后,只要數據連接可用并且Download Manager可用,下載就會開始。
要在下載完成的時候獲得一個系統通知(notification),注冊一個廣播接受者來接收ACTION_DOWNLOAD_COMPLETE廣播,這個廣播會包含一個
EXTRA_DOWNLOAD_ID信息在intent中包含了已經完成的這個下載的ID,代碼片段如下所示:
IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
long reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (myDownloadReference == reference) {
}
}
};
registerReceiver(receiver, filter);
```
>使用Download Manager的openDownloadedFile方法可以打開一個已經下載完成的文件,返回一個ParcelFileDescriptor對象。我們可以通過Download Manager來
查詢下載文件的保存地址,如果在下載時制定了路徑和文件名,我們也可以直接操作文件。
我們可以為ACTION_NOTIFICATION_CLICKED action注冊一個廣播接受者,當用戶從通知欄點擊了一個下載項目或者從Downloads app點擊可一個下載的項目的
時候,系統就會發出一個點擊下載項的廣播。
代碼片段如下:
```
IntentFilter filter = new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String extraID = DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS;
long[] references = intent.getLongArrayExtra(extraID);
for (long reference : references)
if (reference == myDownloadReference) {
// Do something with downloading file.
}
}
};
registerReceiver(receiver, filter);
```
####定制Download Manager Notifications的樣式
>默認情況下,通知欄中會顯示被Download Manager管理的每一個download每一個Notification會顯示當前的下載進度和文件的名字,如下圖所示:
通過Download Manager可以為每一個download request定制Notification的樣式,包括完全隱藏Notification。下面的代碼片段顯示了通過setTitle和setDescription
方法來定制顯示在文件下載Notification中顯示的文字。
```
request.setTitle(“Earthquakes”);
request.setDescription(“Earthquake XML”);
```
>setNotificationVisibility方法可以用來控制什么時候顯示Notification,甚至是隱藏該request的Notification。有以下幾個參數:
**Request.VISIBILITY_VISIBLE**:在下載進行的過程中,通知欄中會一直顯示該下載的Notification,當下載完成時,該Notification會被移除,這是默認的參數值。
**Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED**:在下載過程中通知欄會一直顯示該下載的Notification,在下載完成后該Notification會繼續顯示,直到用戶點擊該
Notification或者消除該Notification。
**Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION**:只有在下載完成后該Notification才會被顯示。
**Request.VISIBILITY_HIDDEN**:不顯示該下載請求的Notification。如果要使用這個參數,需要在應用的清單文件中加上DOWNLOAD_WITHOUT_NOTIFICATION權限。
指定下載保存地址
默認情況下,所有通過Download Manager下載的文件都保存在一個共享下載緩存中,使用系統生成的文件名每一個Request對象都可以制定一個下載
保存的地址,通常情況下,所有的下載文件都應該保存在外部存儲中,所以我們需要在應用清單文件中加上WRITE_EXTERNAL_STORAGE權限:
```
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>
```
>下面的代碼片段是在外部存儲中指定一個任意的保存位置的方法:
```
request.setDestinationUri(Uri.fromFile(f));
```
> f是一個File對象。
如果下載的這個文件是你的應用所專用的,你可能會希望把這個文件放在你的應用在外部存儲中的一個專有文件夾中。注意這個文件夾不提供訪問控制,
所以其他的應用也可以訪問這個文件夾。在這種情況下,如果你的應用卸載了,那么在這個文件夾也會被刪除。
下面的代碼片段是指定存儲文件的路徑是應用在外部存儲中的專用文件夾的方法:
```
request.setDestinationInExternalFilesDir(this,
Environment.DIRECTORY_DOWNLOADS, “Bugdroid.png”);
```
>如果下載的文件希望被其他的應用共享,特別是那些你下載下來希望被Media Scanner掃描到的文件(比如音樂文件),那么你可以指定你的下載路徑在
外部存儲的公共文件夾之下,下面的代碼片段是將文件存放到外部存儲中的公共音樂文件夾的方法:
```
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC,
"Android_Rock.mp3");
```
>在默認的情況下,通過Download Manager下載的文件是不能被Media Scanner掃描到的,進而這些下載的文件(音樂、視頻等)就不會在Gallery和
Music Player這樣的應用中看到。
為了讓下載的音樂文件可以被其他應用掃描到,我們需要調用Request對象的allowScaningByMediaScanner方法。
如果我們希望下載的文件可以被系統的Downloads應用掃描到并管理,我們需要調用Request對象的setVisibleInDownloadsUi方法,傳遞參數true。
取消和刪除下載
Download Manager的remove方法可以用來取消一個準備進行的下載,中止一個正在進行的下載,或者刪除一個已經完成的下載。
remove方法接受若干個download 的ID作為參數,你可以設置一個或者幾個你想要取消的下載的ID,如下代碼段所示:
```
downloadManager.remove(REFERENCE_1, REFERENCE_2, REFERENCE_3);
```
>該方法返回成功取消的下載的個數,如果一個下載被取消了,所有相關聯的文件,部分下載的文件和完全下載的文件都會被刪除。
查詢Download Manager
你可以通過查詢Download Manager來獲得下載任務的狀態,進度,以及各種細節,通過query方法返回一個包含了下載任務細節的Cursor。
query方法傳遞一個DownloadManager.Query對象作為參數,通過DownloadManager.Query對象的setFilterById方法可以篩選我們希望查詢的下
載任務的ID。也可以使用setFilterByStatus方法篩選我們希望查詢的某一種狀態的下載任務,傳遞的參數是DownloadManager.STATUS_*常量,可以指定
正在進行、暫停、失敗、完成四種狀態。
Download Manager包含了一系列COLUMN_*靜態String常量,可以用來查詢Cursor中的結果列索引。我們可以查詢到下載任務的各種細節,包括狀態,
文件大小,已經下載的字節數,標題,描述,URI,本地文件名和URI,媒體類型以及Media Provider download URI。
下面的代碼段是通過注冊監聽下載完成事件的廣播接受者來查詢下載完成文件的本地文件名和URI的實現方法:
```
@Override
public void onReceive(Context context, Intent intent) {
long reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (myDownloadReference == reference) {
Query myDownloadQuery = new Query();
myDownloadQuery.setFilterById(reference);
Cursor myDownload = downloadManager.query(myDownloadQuery);
if (myDownload.moveToFirst()) {
int fileNameIdx =
myDownload.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
int fileUriIdx =
myDownload.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String fileName = myDownload.getString(fileNameIdx);
String fileUri = myDownload.getString(fileUriIdx);
// TODO Do something with the file.
Log.d(TAG, fileName + " : " + fileUri);
}
myDownload.close();
}
}
```
>對于暫停和失敗的下載,我們可以通過查詢COLUMN_REASON列查詢出原因的整數碼。
對于STATUS_PAUSED狀態的下載,可以通過DownloadManager.PAUSED_*靜態常量來翻譯出原因的整數碼,進而判斷出下載是由于等待網絡連接
還是等待WiFi連接還是準備重新下載三種原因而暫停。
對于STATUS_FAILED狀態的下載,我們可以通過DownloadManager.ERROR_*來判斷失敗的原因,可能是錯誤碼(失敗原因)包括沒有存儲設備,
存儲空間不足,重復的文件名,或者HTTP errors。
下面的代碼是如何查詢出當前所有的暫停的下載任務,提取出暫停的原因以及文件名稱,下載標題以及當前進度的實現方法:
```
// Obtain the Download Manager Service.
String serviceString = Context.DOWNLOAD_SERVICE;
DownloadManager downloadManager;
downloadManager = (DownloadManager)getSystemService(serviceString);
// Create a query for paused downloads.
Query pausedDownloadQuery = new Query();
pausedDownloadQuery.setFilterByStatus(DownloadManager.STATUS_PAUSED);
// Query the Download Manager for paused downloads.
Cursor pausedDownloads = downloadManager.query(pausedDownloadQuery);
// Find the column indexes for the data we require.
int reasonIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_REASON);
int titleIdx = pausedDownloads.getColumnIndex(DownloadManager.COLUMN_TITLE);
int fileSizeIdx =
pausedDownloads.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
int bytesDLIdx =
pausedDownloads.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
// Iterate over the result Cursor.
while (pausedDownloads.moveToNext()) {
// Extract the data we require from the Cursor.
String title = pausedDownloads.getString(titleIdx);
int fileSize = pausedDownloads.getInt(fileSizeIdx);
int bytesDL = pausedDownloads.getInt(bytesDLIdx);
// Translate the pause reason to friendly text.
int reason = pausedDownloads.getInt(reasonIdx);
String reasonString = "Unknown";
switch (reason) {
case DownloadManager.PAUSED_QUEUED_FOR_WIFI :
reasonString = "Waiting for WiFi"; break;
case DownloadManager.PAUSED_WAITING_FOR_NETWORK :
reasonString = "Waiting for connectivity"; break;
case DownloadManager.PAUSED_WAITING_TO_RETRY :
reasonString = "Waiting to retry"; break;
default : break;
}
// Construct a status summary
StringBuilder sb = new StringBuilder();
sb.append(title).append("\n");
sb.append(reasonString).append("\n");
sb.append("Downloaded ").append(bytesDL).append(" / " ).append(fileSize);
// Display the status
Log.d("DOWNLOAD", sb.toString());
}
// Close the result Cursor.
pausedDownloads.close();
```
####參考
>[Android系統下載管理DownloadManager功能介紹及使用示例](http://www.trinea.cn/android/android-downloadmanager/)
[Android DownloadManager 的使用](http://blog.csdn.net/carrey1989/article/details/8060155)
[Android快速實現文件下載(只有4行代碼)](http://www.lxweimin.com/p/46fd1c253701)