DownloadManager 简单介绍
1、DownloadManager 是从 Android 2.3(API level 9)开始 Android 用系统服务(Service)的方式提供优化处理长时间的下载操作的类,包含两个静态内部类DownloadManager.Query
和DownloadManager.Request
。DownloadManager.Request
用来请求一个下载,DownloadManager.Query
用来查询下载信息。
2、DownloadManager 主要提供了下面几个接口:
public long enqueue(Request request)
执行下载,返回 downloadId,downloadId 可用于后面查询下载信息。若网络不满足条件、Sdcard 挂载中、超过最大并发数等异常会等待下载,正常则直接下载。public int remove(long… ids)
删除下载,若下载中取消下载。会同时删除下载文件和记录。public Cursor query(Query query)
查询下载信息。
3、DownloadManager 处理 HTTP 连接并监控连接中的状态变化以及系统重启来确保每一个下载任务顺利完成。在大多数涉及到下载的情况中使用 Download Manager 都是不错的选择,特别是当用户切换不同的应用以后下载需要在后台继续进行,以及当下载任务顺利完成非常重要的情况(DownloadManager 对于断点续传功能支持很好)。
4、使用 DownloadManager 类下载文件
Uri uri = Uri.parse("http://app.58eid.com/realname.apk");
DownloadManager.Request request = new Request(uri);
// 通过 setAllowedNetworkTypes 方法可以设置允许在何种网络下下载,
// 也可以使用 setAllowedOverRoaming 方法,它更加灵活
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
// 此方法表示在下载过程中通知栏会一直显示该下载,在下载完成后仍然会显示,
// 直到用户点击该通知或者消除该通知。还有其他参数可供选择
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
// 禁止发出通知,既后台下载
//req.setShowRunningNotification(false);
// 不显示下载界面
//req.setVisibleInDownloadsUi(false);
// 设置下载文件存放的路径,同样你可以选择以下方法存放在你想要的位置。我这里放在根目录下面
// setDestinationUri
// setDestinationInExternalPublicDir
request.setDestinationInExternalFilesDir(MainActivity.this, Environment.DIRECTORY_DOWNLOADS, "/");
// 设置一些基本显示信息
request.setTitle("realname.apk");
request.setDescription("实名宝专注于实名认证");
request.setMimeType("application/vnd.android.package-archive");
// 开启下载并返回下载请求的 downloadId
downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
long downloadId = downloadManager.enqueue(request);
定制 Download Manager Notifications 的样式
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
。
设置允许下载的网络类型
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
默认在任何网络下都允许下载。有 NETWORK_MOBILE、NETWORK_WIFI、NETWORK_BLUETOOTH 三种及其组合可供选择。如果只允许 wifi 下载,而当前网络为 3g,则下载会等待。
request.setAllowedOverRoaming(boolean allow)
移动网络情况下是否允许漫游。
调用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);
取消和删除下载
downloadManager.remove(downloadId); // remove 可以同时接受多个下载 id
该方法返回成功取消的下载的个数,如果一个下载被取消了,所有相关联的文件,部分下载的文件和完全下载的文件都会被删除。
查询 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。
通过注册监听下载完成事件的广播接受者来查询下载完成的本地文件名和 url。
@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。
下面的代码是如何查询出当前所有的暂停的下载任务,提取出暂停的原因以及文件名称,下载标题以及当前进度的实现方法:
Query pausedDownloadQuery = new Query();
pausedDownloadQuery.setFilterByStatus(DownloadManager.STATUS_PAUSED);
// 查找所有暂停中的下载文件
Cursor pausedDownloads = downloadManager.query(pausedDownloadQuery);
// 添加索引数据
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);
// 遍历查询 Cursor.
while (pausedDownloads.moveToNext()) {
//从Cursor中查找我们想要的数据
String title = pausedDownloads.getString(titleIdx);
int fileSize = pausedDownloads.getInt(fileSizeIdx);
int bytesDL = pausedDownloads.getInt(bytesDLIdx);
// 查找暂停原因
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;
}
// 显示查询结果
StringBuilder sb = new StringBuilder();
sb.append(title).append("\n");
sb.append(reasonString).append("\n");
sb.append("Downloaded ").append(bytesDL).append(" / " ).append(fileSize);
// 显示状态
Log.e("DOWNLOAD", sb.toString());
}
// 关闭 Cursor.
pausedDownloads.close();
下载管理总结:
下载需要添加的权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
下载进度状态监听代码
其中我们会监听
Uri.parse("content://downloads/my_downloads")
。然后查询下载状态和进度,发送 handler 进行更新,handler 中处理就是设置进度条和状态等。调用
DownloadManager.Request
开始下载下载进度状态查询代码
从上面代码可以看出我们主要调用
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
排序。
下载成功监听
下载完成后,下载管理会发出
DownloadManager.ACTION_DOWNLOAD_COMPLETE
这个广播,并传递downloadId
作为参数。通过接受广播我们可以打开对下载完成的内容进行操作。
(完)