一、 // 下载时,不显示通知栏
downRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
因此在AndroidManifest.xml文件中加入上面所需要的权限即可。
二、9.0 如果访问http网址,记得加上
android:usesCleartextTraffic="true"
三、注册监听器ContentObserver(必须在执行下载前注册内容监听者)
三个参数分别是所要监听的Uri、false表示精确匹配此Uri,true表示可以匹配其派生的Uri、ContentObserver的派生类实例。
一、基础(粗粗看一下就行,看代码一下就知道怎样用)
DownloadManager简介
DownloadManager是Android 2.3(API level 9)用系统服务(Service)的方式提供了DownloadManager来处理长时间的下载操作。它包含两个静态内部类DownloadManager.Query(用来查询下载信息)和DownloadManager.Request(用来请求一个下载)。
DownloadManager主要提供了下面几个方法:
public long enqueue(Request request)把任务加入下载队列并返回downloadId,以便后面用于查询下载信息。若网络不满足条件、Sdcard挂载中、超过最大并发数等异常会等待下载,正常则直接下载。
public int remove(long… ids)删除下载,若取消下载,会同时删除下载文件和记录。
public Cursor query(Query query)查询下载信息,包括下载文件总大小,已经下载的大小以及下载状态等。
ContentObserver简介
public void ContentObserver(Handler handler) 所有ContentObserver的派生类都需要调用该构造方法,参数:handler Handler对象用于在主线程中修改UI。
public void onChange(boolean selfChange)当观察到的Uri中内容发生变化时,就会回调该方法。所有ContentObserver的派生类都需要重载该方法去处理逻辑。
观察特定Uri的步骤如下:
1、创建我们特定的ContentObserver派生类,必须重载父类构造方法,必须重载onChange()方法去处理回调后的功能实现。
2、为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理,调用registerContentObserver()方法去注册内容观察者。
3、由于ContentObserver的生命周期不同步于Activity和Service等。因此,在不需要时,需要手动的调用unregisterContentObserver()注销内容观察者。
二、通过广播接收者监听下载状态
public class DownUtils {
private DownResultListener downResultListener=null;
private static final String TAG = "DownUtils";
//下载器
private DownloadManager downloadManager=null;
//下载的ID
private long downloadId=-1;
private String pathstr="";
private boolean downingFlag=false;
/**
* 下载apk 每个实例,同一时间只可以有一个下载任务
* @param mContext
* @param url
* @param downFileName 下载的文件名,要带后缀,例如:myjst.apk
* @param showInNotify
* @param notifyTitle
* @param notifyMsg
* @param listener
* @return
*/
public boolean downloadTask(Context mContext, String url, String downFileName,
boolean showInNotify, String notifyTitle, String notifyMsg, DownResultListener listener) {
if(downingFlag){
return false;
}else{
downingFlag=true;
}
downloadId=-1;
this.downResultListener=listener;
//创建下载任务
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
//移动网络情况下是否允许漫游
request.setAllowedOverRoaming(false);
// 设置通知是否显示
if(showInNotify){
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}else{
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
}
// 要加下面权限
//
//request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
//设置下载中通知栏提示的标题
request.setTitle(notifyTitle);
//设置下载中通知栏提示的介绍
request.setDescription(notifyMsg);
request.setVisibleInDownloadsUi(true);
// 通过setAllowedNetworkTypes方法可以设置允许在何种网络下下载,
// 也可以使用setAllowedOverRoaming方法,它更加灵活
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
//request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
// 文件类型
//request.setMimeType("application/zip");
//设置下载的路径
File file = new File(mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), downFileName);
request.setDestinationUri(Uri.fromFile(file));
pathstr = file.getAbsolutePath();
//request.setDestinationInExternalPublicDir("DownloadManagerTest", "myjst.apk");
//获取DownloadManager
if (downloadManager == null)
downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
//将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等
if (downloadManager != null) {
downloadId = downloadManager.enqueue(request);
}else{
downingFlag=false;
return false;
}
//如果是普通的下载,可以在这里注册广播接收者
//apk升级功能,如果是在通知栏后台下载 ,一般放在application中注册(注销)广播接收者
//因为启动下载后,用户可能在app中的任意页面。
//解决方法,
// 1、在application中注册(注销)广播接收者
// 2、下载apk任务的id=-1 保存为全局变量,如果为id==-1,监听器不做处理
//注册广播接收者,监听下载状态
mContext.registerReceiver(receiver,
new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
return true;
}
//广播监听下载的各个状态
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(downloadId==-1){
return;
}
checkStatus(context);
}
};
//检查下载状态
private void checkStatus(Context context) {
DownloadManager.Query query = new DownloadManager.Query();
//通过下载的id查找
query.setFilterById(downloadId);
Cursor cursor = downloadManager.query(query);
if (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
//下载暂停
case DownloadManager.STATUS_PAUSED:
Log.e(TAG, "queryUri: STATUS_PAUSED");
break;
//下载延迟
case DownloadManager.STATUS_PENDING:
Log.e(TAG, "queryUri: STATUS_PENDING");
break;
//正在下载
case DownloadManager.STATUS_RUNNING:
Log.e(TAG, "queryUri: STATUS_RUNNING");
break;
//下载完成
case DownloadManager.STATUS_SUCCESSFUL:
Log.e(TAG, "queryUri: STATUS_SUCCESSFUL");
if(downResultListener!=null){
downResultListener.onSuccess(context,pathstr);
downResultListener=null;
}
downloadId=-1;
context.unregisterReceiver(receiver);
downingFlag=false;
break;
//下载失败
case DownloadManager.STATUS_FAILED:
Log.e(TAG, "queryUri: STATUS_FAILED");
if(downResultListener!=null){
downResultListener.onFail(context,"失败");
downResultListener=null;
}
downloadId=-1;
context.unregisterReceiver(receiver);
downingFlag=false;
break;
}
}
cursor.close();
}
public interface DownResultListener{
public void onSuccess(Context context,String filePath);
public void onFail(Context context,String msg);
}
}
调用方法:
new DownUtils().downloadTask(this, PATH, "myjst.apk", true
,"title","msg des` ", new DownUtils.DownResultListener() {
@Override
public void onSuccess(Context context, String filePath) {
Tools.installAPK(context,new File(filePath));
Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onFail(Context context, String msg) {
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
}
});
三、实时获取下载的进度。(这个demo只是练习,写得很粗糙,实战时要自行修改)
public class DownLoadActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String PATH = "http://xxx/rch.apk";
private long mId = -1;
private DownloadManager mDownloadManager;
private DownloadManager.Query mQuery;
private Uri mUri;
private Handler myHandler=new Handler();
private DownloadStatusObserver observer = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_load);
// 获取DownloadManager对象
mDownloadManager = (DownloadManager) this.getSystemService(DOWNLOAD_SERVICE);
observer = new DownloadStatusObserver(myHandler);
//在执行下载前注册内容监听者
registerContentObserver();
}
/**
* 下载点击
*/
public void downLoad(View view) {
doSuccess=false;
/* 移到onCreate
// 获取DownloadManager对象
mDownloadManager = (DownloadManager) this.getSystemService(DOWNLOAD_SERVICE);
observer = new DownloadStatusObserver(myHandler);
//在执行下载前注册内容监听者
registerContentObserver();*/
// 创建一个下载请求
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(PATH));
// 设置内存卡保存的文件夹与文件名
// setDestinationUri
// setDestinationInExternalPublicDir
request.setDestinationInExternalPublicDir("DownloadManagerTest", "myjst.apk");
//设置下载中通知栏提示的标题
request.setTitle("python");
//设置下载中通知栏提示的介绍
request.setDescription("python desc");
// 设置通知是否显示
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
// 通过setAllowedNetworkTypes方法可以设置允许在何种网络下下载,
// 也可以使用setAllowedOverRoaming方法,它更加灵活
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
request.setVisibleInDownloadsUi(true);
// 文件类型
request.setMimeType("application/zip");
// 执行下载,返回downloadId,downloadId可用于后面查询下载信息。若网络不满足条件、Sdcard挂载中、超过最大并发数等异常会等待下载,正常则直接下载。
mId = mDownloadManager.enqueue(request);
// 返回移动网络下载的最大值
Long maxBytesOverMobile = DownloadManager.getMaxBytesOverMobile(this);
Log.e(TAG, "downLoad: maxBytesOverMobile ---------" + maxBytesOverMobile);
// 获取建议的移动网络下载的大小
Long recommendedMaxBytesOverMobile = DownloadManager.getRecommendedMaxBytesOverMobile(this);
Log.e(TAG, "downLoad: recommendedMaxBytesOverMobile ---------" + recommendedMaxBytesOverMobile);
}
/**
* 注册ContentObserver
*/
private void registerContentObserver() {
/** observer download change **/
if (observer != null) {
//三个参数分别是所要监听的Uri、false表示精确匹配此Uri,true表示可以匹配其派生的Uri、ContentObserver的派生类实例
getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true, observer);
}
}
/**
* 取消下载点击
*/
public void unDownLoad(View view) {
// 删除下载,若下载中取消下载。会同时删除下载文件和记录。
mDownloadManager.remove(mId);
}
/**
* 查询存储地址
*/
public void queryUri(View view) {
queryDownFileInfo();
}
private void queryDownFileInfo() {
// 获取Uri和下载文件
mUri = mDownloadManager.getUriForDownloadedFile(mId);
/* if(mUri!=null){
getContentResolver().unregisterContentObserver(observer);
getContentResolver().registerContentObserver(mUri, true, observer);
}*/
Log.e(TAG, "queryUri: uri--------" + mUri);
// 创建一个查询对象
mQuery = new DownloadManager.Query().setFilterById(mId);
// 根据查询对象获取一个游标对象
Cursor cursor = mDownloadManager.query(mQuery);
if (null != cursor) {
if (cursor.moveToFirst()) {
// 获取文件路径
String fileUri = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));
Log.e(TAG, "queryUri: fileUri ---------" + fileUri);
// 获取下载状态
int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
Log.e(TAG, "queryUri: " + status);
logStatus(status);
}
cursor.close();
}
}
/**
* 获取下载信息
*
* @param downloadId 下载ID
* @return
*/
public int[] getBytesAndStatus(long downloadId) {
int[] bytesAndStatus = new int[]{-1, -1, 0};
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = null;
try {
c = mDownloadManager.query(query);
if (c != null && c.moveToFirst()) {
// 获取已下载字节数
bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
// 获取总字节数
bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
// 获取当前状态
bytesAndStatus[2] = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} finally {
if (c != null) {
c.close();
}
}
return bytesAndStatus;
}
class DownloadStatusObserver extends ContentObserver {
DownloadStatusObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
try {
if(mId!=-1){
int[] bytesAndStatus = getBytesAndStatus(mId);
//当前大小
int currentSize = bytesAndStatus[0];
//总大小
int totalSize = bytesAndStatus[1];
//下载状态
int status = bytesAndStatus[2];
Log.e(TAG, "当前大小=" + currentSize + ",总大小=" + totalSize + ",status=" + status);
/*if(i++>100){
Log.e(TAG, "当前大小=" + currentSize + ",总大小=" + totalSize + ",status=" + status);
i=0;
}*/
//下载成功时,onChange的方法会调用多次,所以使用时要注意
logStatus(status);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private boolean doSuccess=false;
private void logStatus(int status) {
switch (status) {
// 空闲等待中
case DownloadManager.STATUS_PENDING:
Log.e(TAG, "queryUri: STATUS_PENDING");
break;
// 暂停中
case DownloadManager.STATUS_PAUSED:
Log.e(TAG, "queryUri: STATUS_PAUSED");
break;
// 正在下载
case DownloadManager.STATUS_RUNNING:
Log.e(TAG, "queryUri: STATUS_RUNNING");
break;
// 下载成功
case DownloadManager.STATUS_SUCCESSFUL:
Log.e(TAG, "queryUri: STATUS_SUCCESSFUL");
if(!doSuccess){
doSuccess=true;
queryDownFileInfo();
}
break;
// 下载失败
case DownloadManager.STATUS_FAILED:
Log.e(TAG, "queryUri: STATUS_FAILED");
break;
default:
break;
}
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(observer);
}
}
xml:
参考文章:
1、Android DownLoadManager 使用 ,例子有bug
2、DownloadManager实现版本更新,监听下载进度