原文地址:https://blog.stylingandroid.com/downloadmanager-part-1/
在App中一個相當普遍的操作就是下載內容到我們的設備中,雖然通過網(wǎng)絡下載所需要的內容是很簡單的,但是還是有另外一種構建方法,如果下載下來的內容可以與其他的應用共享或者存儲到設備上一個公共的位置的話將會是非常有用的,在這一系列文章中,將看一下DownloadManager,他是一個易于使用的API,并且能夠解決很多常見的問題。
在我們深入了解之前,先提一下,DownloadManager在API 9以來就一直存在,雖然從那之后有一些小的調整,但是大體上已經(jīng)是相當穩(wěn)定的了。
現(xiàn)在創(chuàng)建個簡單的Activity來包含我們的UI,他包含一個Button,當點擊的時候,可以下載一個PDF文件。
重要的一點必須說明的是有一個Downloader
對象包含所有的DownloadManager的邏輯,并且當下載成功的時候里面的fileDownloaded()
方法會被回調。還有一個很重要的事情就是我們需要在onDestroy
中調用Downloader
的unregister()
方法。
public class MainActivity extends AppCompatActivity implements Downloader.Listener {
private static final String URI_STRING = "http://www.cbu.edu.zm/downloads/pdf-sample.pdf";
private Button download;
private Downloader downloader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
download = (Button) findViewById(R.id.download);
download.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
downloadOrCancel();
}
});
downloader = Downloader.newInstance(this);
}
void downloadOrCancel() {
if (downloader.isDownloading()) {
cancel();
} else {
download();
}
updateUi();
}
private void cancel() {
downloader.cancel();
}
private void download() {
Uri uri = Uri.parse(URI_STRING);
downloader.download(uri);
}
@Override
public void fileDownloaded(Uri uri, String mimeType) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
startActivity(intent);
updateUi();
}
@Override
public Context getContext() {
return getApplicationContext();
}
希望上面的代碼是簡單明了的,接著我們會繼續(xù)探索DownloadManager的神奇,并更好的了解在 fileDownloaded
中做了什么。
現(xiàn)在讓我們深入了解Downloader類,看一下我們該如何使用DownloadManager
class Downloader implements DownloadReceiver.Listener {
private final Listener listener;
private final DownloadManager downloadManager;
private DownloadReceiver receiver = null;
private long downloadId = -1;
static Downloader newInstance(Listener listener) {
Context context = listener.getContext();
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
return new Downloader(downloadManager, listener);
}
Downloader(DownloadManager downloadManager, Listener listener) {
this.downloadManager = downloadManager;
this.listener = listener;
}
void download(Uri uri) {
if (!isDownloading()) {
register();
DownloadManager.Request request = new DownloadManager.Request(uri);
downloadId = downloadManager.enqueue(request);
}
}
boolean isDownloading() {
return downloadId >= 0;
}
void register() {
if (receiver == null && isDownloading()) {
receiver = new DownloadReceiver(this);
receiver.register(listener.getContext());
}
}
@Override
public void downloadComplete(long completedDownloadId) {
if (downloadId == completedDownloadId) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
downloadId = -1;
unregister();
Cursor cursor = downloadManager.query(query);
while (cursor.moveToNext()) {
getFileInfo(cursor);
}
cursor.close();
}
}
void unregister() {
if (receiver != null) {
receiver.unregister(listener.getContext());
}
receiver = null;
}
private void getFileInfo(Cursor cursor) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
Long id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
Uri uri = downloadManager.getUriForDownloadedFile(id);
String mimeType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
listener.fileDownloaded(uri, mimeType);
}
}
void cancel() {
if (isDownloading()) {
downloadManager.remove(downloadId);
downloadId = -1;
unregister();
}
}
interface Listener {
void fileDownloaded(Uri uri, String mimeType);
Context getContext();
}
}
可以通過調用getSystemService(Context.DOWNLOAD_SERVICE)
來獲取一個DownloadManager的實例。
初始化一個簡單的下載并且在download()
方法中執(zhí)行,我們首先注冊了一個BroadcastReceiver
用來處理DownloadManager一旦下載成功的回調,最后我們通過Uri構造一個DownloadManager.Request
,最后,將下載請求加入到DownloadManager中,同時返回一個唯一的ID,用來標識我們此次的下載請求。
我們可以調用cancel()
方法來取消下載請求并且使用ID將此次下載從DownloadManager中移除掉。
我們在Activity中onDestroy()
中調用的unregister()
方法使用來取消注冊BroadcastReceiver的。很重要的一點事,我們不能保證在退出應用程序的時候能夠下載完成,這也是使用DownloadManager的好處之一,我們不需要創(chuàng)建后臺服務來執(zhí)行下載任務,DownloadManager已經(jīng)為我們做過了,在Activity中的onDestroy取消下載視為了確保,當App在未激活的狀況下下載完成我們不用去嘗試做任何事情。
downloadComplete()
將在下載完成時由BroadcastReceiver調用,首先會檢查他是否匹配我們的下載Id,如果一致的話,我們會查詢DownloadManager然后獲取當前的下載信息,如果是多個任務同時下載的話,那么我們需要定義多個下載ID在查詢中,然后Cursor會返回多條數(shù)據(jù),在上面的例子中,就下載了一個任務,但是那些代碼同樣適用于多個任務的情況。
首先我們需要提取下載狀態(tài)的字段,用來指示下載的狀態(tài),在我們的情況下,只有在下載完成時會被觸發(fā),所以我們只需要檢查下載是否成功。然而在一個完整的實現(xiàn)中,恢復之前啟動應用的下載狀態(tài)將會非常有用。
一旦下載成功的時候,我們可以提取內容的本地Uri(我們下載所存儲的位置)和MIME 類型,轉換之后返回給我們的監(jiān)聽者。
讓我們看一下BroadcastReceiver的代碼:
class DownloadReceiver extends BroadcastReceiver {
private final Listener listener;
DownloadReceiver(Listener listener) {
this.listener = listener;
}
@Override
public void onReceive(Context context, Intent intent) {
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
listener.downloadComplete(downloadId);
}
public void register(Context context) {
IntentFilter downloadFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
context.registerReceiver(this, downloadFilter);
}
public void unregister(Context context) {
context.unregisterReceiver(this);
}
interface Listener {
void downloadComplete(long downloadId);
}
}
這里值得注意的是我們需要注冊的操作是DownloadManager.ACTION_DOWNLOAD_COMPLETE
,然后將被下載完成的事件喚醒,我們也可以注冊其他事件,例如用戶點擊下載通知事件,可以將用戶直接帶到應用中來控制或者查看下載的狀態(tài),但是通常我們只對完成感興趣。
還有一個值得注意的是,當我們接收到下載完成的Intent時,可以從Intent extras中獲取downloadId。
@Override
public void fileDownloaded(Uri uri, String mimeType) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
startActivity(intent);
updateUi();
}
下一篇文章,我們將會深入的剖析DownloadManager更多強大的功能。