DownloadManger是android 2.3(api level 9)开始 提供的用于优化处理长时间的下载操作。DownloadManager 处理Http/Https连接并监控连接中的状态变化及系统重启来确保每一个下载任务顺利完成。
一、代码配置
<!--网络通信权限-->
<uses-permission android:name="android.permission.INTERNET"/>
<!--SD卡写入数据权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--SD卡创建与删除权限-->
<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"/>
二、调用服务
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("ttt", "下载进度:" + fraction);
//判断是否真的下载完成进行安装了,以及是否注册绑定过服务
if (fraction == DownloadService.UNBIND_SERVICE ) {
unbindService(conn);
}
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
public void bindService(String apkUrl,String name) {
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra(DownloadService.BUNDLE_KEY_DOWNLOAD_URL, apkUrl);
intent.putExtra(DownloadService.BUNDLE_KEY_FILE_NAME, name);
bindService(intent, conn, BIND_AUTO_CREATE);
}
三、DownloadService代码
public class DownloadService extends Service {
private static final String TAG = DownloadService.class.getSimpleName();
public static final int HANDLE_DOWNLOAD = 0x001;
public static final String BUNDLE_KEY_DOWNLOAD_URL = "download_url";
public static final String BUNDLE_KEY_FILE_NAME = "";
public static final float UNBIND_SERVICE = 2.0F;
private DownloadBinder binder;
private DownloadManager downloadManager;
private DownloadChangeObserver downloadObserver;
private BroadcastReceiver downLoadBroadcast;
private ScheduledExecutorService scheduledExecutorService;
//下载任务ID
private long downloadId;
private String downloadUrl,file_name;
private OnProgressListener onProgressListener;
public Handler downLoadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (onProgressListener != null && HANDLE_DOWNLOAD == msg.what) {
//被除数可以为0,除数必须大于0
if (msg.arg1 >= 0 && msg.arg2 > 0) {
onProgressListener.onProgress(msg.arg1 / (float) msg.arg2);
}
}
}
};
private Runnable progressRunnable = new Runnable() {
@Override
public void run() {
updateProgress();
}
};
@Override
public void onCreate() {
super.onCreate();
binder = new DownloadBinder();
}
@Override
public IBinder onBind(Intent intent) {
downloadUrl = intent.getStringExtra(BUNDLE_KEY_DOWNLOAD_URL);
file_name = intent.getStringExtra(BUNDLE_KEY_FILE_NAME);
LogUtil.i(TAG, "Apk下载路径传递成功:" + downloadUrl);
downloadApk(downloadUrl);
return binder;
}
/**
* 下载最新APK
*/
private void downloadApk(String url) {
//获取DownloadManager
downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
downloadObserver = new DownloadChangeObserver();
registerContentObserver();
//创建下载任务
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
//移动网络情况下是否允许漫游
request.setAllowedOverRoaming(false);
//在通知栏中显示,默认就是显示的
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
request.setTitle("新版本Apk");
request.setDescription("Apk Downloading");
request.setVisibleInDownloadsUi(true);
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
//设置下载的路径
request.setDestinationInExternalPublicDir(getApplicationContext().getPackageName()+"/file" , file_name);
//将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等
downloadId = downloadManager.enqueue(request);
registerBroadcast();
}
/**
* 注册广播
*/
private void registerBroadcast() {
/**注册service 广播 1.任务完成时 2.进行中的任务被点击*/
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED);
registerReceiver(downLoadBroadcast = new DownLoadBroadcast(), intentFilter);
}
/**
* 注销广播
*/
private void unregisterBroadcast() {
if (downLoadBroadcast != null) {
unregisterReceiver(downLoadBroadcast);
downLoadBroadcast = null;
}
}
/**
* 注册ContentObserver
*/
private void registerContentObserver() {
/** observer download change **/
if (downloadObserver != null) {
getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), false, downloadObserver);
}
}
/**
* 注销ContentObserver
*/
private void unregisterContentObserver() {
if (downloadObserver != null) {
getContentResolver().unregisterContentObserver(downloadObserver);
}
}
/**
* 关闭定时器,线程等操作
*/
private void close() {
if (scheduledExecutorService != null && !scheduledExecutorService.isShutdown()) {
scheduledExecutorService.shutdown();
}
if (downLoadHandler != null) {
downLoadHandler.removeCallbacksAndMessages(null);
}
}
/**
* 发送Handler消息更新进度和状态
*/
private void updateProgress() {
int[] bytesAndStatus = getBytesAndStatus(downloadId);
downLoadHandler.sendMessage(downLoadHandler.obtainMessage(HANDLE_DOWNLOAD, bytesAndStatus[0], bytesAndStatus[1], bytesAndStatus[2]));
}
/**
* 通过query查询下载状态,包括已下载数据大小,总大小,下载状态
*
* @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));
//下载状态
bytesAndStatus[2] = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return bytesAndStatus;
}
/**
* 接受下载完成广播
*/
private class DownLoadBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long downId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
switch (intent.getAction()) {
case DownloadManager.ACTION_DOWNLOAD_COMPLETE:
if (downloadId == downId && downId != -1 && downloadManager != null) {
Uri downIdUri = downloadManager.getUriForDownloadedFile(downloadId);
close();
if (downIdUri != null) {
LogUtil.i(TAG, "广播监听下载完成,APK存储路径为 :" + downIdUri.getPath());
// SPUtil.put(Constant.SP_DOWNLOAD_PATH, downIdUri.getPath());
// APPUtil.installApk(context, downIdUri);
}
if (onProgressListener != null) {
onProgressListener.onProgress(UNBIND_SERVICE);
}
}
break;
default:
break;
}
}
}
/**
* 监听下载进度
*/
private class DownloadChangeObserver extends ContentObserver {
public DownloadChangeObserver() {
super(downLoadHandler);
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
}
/**
* 当所监听的Uri发生改变时,就会回调此方法
*
* @param selfChange 此值意义不大, 一般情况下该回调值false
*/
@Override
public void onChange(boolean selfChange) {
scheduledExecutorService.scheduleAtFixedRate(progressRunnable, 0, 2, TimeUnit.SECONDS);
}
}
public class DownloadBinder extends Binder {
/**
* 返回当前服务的实例
*
* @return
*/
public DownloadService getService() {
return DownloadService.this;
}
}
public interface OnProgressListener {
/**
* 下载进度
*
* @param fraction 已下载/总大小
*/
void onProgress(float fraction);
}
/**
* 对外开发的方法
*
* @param onProgressListener
*/
public void setOnProgressListener(OnProgressListener onProgressListener) {
this.onProgressListener = onProgressListener;
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterBroadcast();
unregisterContentObserver();
LogUtil.i(TAG, "下载任务服务销毁");
}
}
四.DownloadManager/Request/Query 接口参考
DownloadManager:
long enqueue(Request request)
将一个新的下载项加入队列,该下载项将在downloadmanager准备好执行该下载项/连接可用时自动开始下载,返回该下载的唯一id
int remove(long... ids)
移除一个或多个下载项,返回改变的个数
Cursor query(Query query)
执行一个query Qurey类下面有介绍
openDownloadedFile(long id)
打开一个文件(该文件必须已下载完成)
getUriForDownloadedFile(long id)
返回一个已下载项的Uri,如:content://downloads/my_downloads/103
getMimeTypeForDownloadedFile(long id)
返回一个已下载项mimetype
restartDownload(long... ids)
重新已完成的下载项(下载成功,下载失败)开始下载;
getMaxBytesOverMobile(Context context)
返回移动网络下载的最大值
rename(Context context, long id, String displayName)
重命名已下载项的名字
getRecommendedMaxBytesOverMobile(Context context)
获取建议的移动网络下载的大小
addCompletedDownload(String title, String description,boolean isMediaScannerScannable, String mimeType, String path, long length,boolean showNotification)
添加一个文件到系统的下载文件数据库中,并且可以在系统下载app中显示出该文件的下载条目;
根据api上的说明,该方法对于使一个文件成为被MediaScanner可扫描的文件很有用,调用该方法,可将指定的文件变成scannable by MediaScanner by setting the param isMediaScannerScannable to true.如相册gallary应用中就很有用
Request
setDestinationUri(Uri uri) :
自定义下载本地路径,传入一个Uri类型;
setDestinationInExternalFilesDir(Context context, String dirType,String subPath) :
自定义下载本地路径,为内置sd卡的Android/data/package-name/ 父目录下,该目录下的内容会随应用的卸载而删除清空数据,dirType:文件的类型(Environment.DIRECTORY_DOWNLOADS,Environment.DIRECTORY_DCIM,……);
subPath:父目录下得子目录路径(/apk/myPath/myapk.apk)
setDestinationInExternalPublicDir(String dirType, String subPath) :
同上,下载到sd卡上共享的公共父目录下的subPath子目录下
allowScanningByMediaScanner() :
从字面意思可以明白,是允许该下载项可以被MediaScanner(多媒体文件的扫描工作)扫描到。(MediaScanner认识:http://blog.csdn.net/hellofeiya/article/details/8255898)
addRequestHeader(String header, String value) :
添加一个Http请求头信息到http头信息列表末尾
setTitle(CharSequence title)/setDescription(CharSequence description)
设置通知栏中下载通知的显示样式
setMimeType(String mimeType)
Set the MIME content type of this download. This will override the content type declared in the server’s response.
设置MimeType,会覆盖服务器返回的已声明的content type
setShowRunningNotification(boolean show)(已过时)
设置开始下载时是否通过通知的形式显示该下载项状态,默认显示
setNotificationVisibility(int visibility)替代上面方法
visibility取值:
{@link #VISIBILITY_HIDDEN}, {@link #VISIBILITY_VISIBLE}, {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}.
setAllowedNetworkTypes(int flags)
设置下载时允许的网络环境,默认允许所有网络环境
setAllowedOverRoaming(boolean allow)
Set whether this download may proceed over a metered network connection. By default, metered networks are allowed.
设置是否允许漫游网络下的下载,默认允许漫游下载
setRequiresCharging(boolean requiresCharging)
设置是否必须充电状态下下载,默认否
setRequiresDeviceIdle(boolean requiresDeviceIdle)
设置是否必须手机处于空闲状态下载,默认否
setVisibleInDownloadsUi(boolean isVisible)
设置下载项是否在系统的 下载 app中显示 默认显示,打开系统自带下载app可查看该下载项
Query
setFilterById(long... ids)
根据id对downloadmanager中的下载项进行筛选,返回cursor
setFilterByStatus(int flags)
根据status对downloadmanager中的下载项进行筛选,返回cursor
setOnlyIncludeVisibleInDownloadsUi(boolean value)
设置为true,则是对仅仅显示在下载界面的下载项进行筛选,反之,未显示的也将被筛选
orderBy(String column, int direction)
对返回的Cursor所携带的数据根据column,direction进行排序
column取值 COLUMN_LAST_MODIFIED_TIMESTAMP
COLUMN_TOTAL_SIZE_BYTES
direction取值 ORDER_ASCENDING
ORDER_DESCENDING
喜欢 就关注吧,欢迎投稿!
如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: http://leetcode.jp/downloadmanager使用详解/