DownloadManager使用比较简单,至于版本大小判断就不说了,直接就贴代码,需要更新执行下面代码就可以了
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(newApkDownloadUrl));
//设置Notification的标题和描述
request.setTitle("应用名");
request.setDescription("新版本3.5.2下载中...");
//设置漫游状态下是否可以下载
request.setAllowedOverRoaming(false);
request.setVisibleInDownloadsUi(true);
//设置Notification的显示,和隐藏。
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setMimeType("application/vnd.android.package-archive");
request.setDestinationUri(Uri.fromFile(new File(apk_filepath)));
downloadId = downloadManager.enqueue(request);
上面newApkDownloadUrl 就是服务器Apk地址,apk_filepath就是手机下载之后存放路径。
建议存储在App缓存路径内,不建议存储在外部Sd卡,外部需要读写权限,并且存App缓存路径有个好处,就是当卸载App后路径下的Apk也会被清理掉;还有需要兼容7.0之后路径问题,简单贴代码,具体适配请了解7.0特性
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
downloadUri = FileProvider.getUriForFile(getContext(), getContext().getPackageName()+".fileprovider", new File(apk_filepath));
}else{
downloadUri = Uri.fromFile(new File(apk_filepath));
}
据说部分手机对DownloadManager进行了阉割,可能用不了,所以下载前先判断功能是否可用,贴代码
/**
* 系统的下载组件是否可用
*
* @return boolean
*/
public static boolean checkDownloadState(Context context) {
try {
int state = context.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
if (state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|| state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
|| state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
如果可用就运行下载代码,如果不可用则把apkDownloadUrl丢给浏览器下载
/**
* 跳转浏览器下载
*/
private void openBrowser(){
Intent intent = new Intent(Intent.ACTION_VIEW);//使用浏览器下载
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(newApkDownloadUrl));
mContext.get().startActivity(intent);
}
重复下载或者下载失败问题,如果正在下载用户重复点击了下载怎么办?或者下载已经完成了用户又点击下载等问题,所以进行下载前需要做判断,判断安装包状态,贴代码:
//获取存储的下载ID
long downloadId = SPCache.getObject(mContext.get(),DownloadManager.EXTRA_DOWNLOAD_ID,Long.class,-1L);
if(downloadId != -1) {
//获取当前状态
int status = getDownloadStatus(downloadId);
switch (status) {
//下载延迟
case DownloadManager.STATUS_PENDING:
//正在下载
case DownloadManager.STATUS_RUNNING:
Log.d("DownloadHelper", "apk is already downloading");
if(!isDownloading(newApkDownloadUrl)){
startDownload(zhsApplication);
}
break;
//下载完成
case DownloadManager.STATUS_SUCCESSFUL:
//状态为下载成功
//获取下载路径URI
Uri downloadUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
downloadUri = FileProvider.getUriForFile(ZHSApplication.getContext(), ZHSApplication.getContext().getPackageName()+".fileprovider", new File(apk_filepath));
}else{
downloadUri = Uri.fromFile(new File(apk_filepath));
}
if(null != downloadUri) {
//存在下载的APK,如果两个APK相同,启动更新界面。否之则删除,重新下载。
if(compare(getApkInfo(mContext.get(),apk_filepath),mContext.get())) {
installAPK();
} else {
//删除下载任务以及文件
downloadManager.remove(downloadId);
startDownload(zhsApplication);
}
}else{
startDownload(zhsApplication);
}
break;
default:
//取消或者没有记录//下载失败,重新下载
startDownload(zhsApplication);
break;
}
} else {
//不存在downloadId,没有下载过APK
startDownload(zhsApplication);
}
这个就是下载前的判断,SPCache就是一个本地保存信息的一个工具,当请求
downloadManager.enqueue(request) 时保存一下downloadId,方便获取下载状态。
其次是下载完成监听,请求完下载就开始注册BroadcastReceiver进行监听
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
checkStatus();
}
}
};
下载完成后判断下载状态进行安装就可以了
private void installAPK() {
setPermission(apk_filepath);
Intent intent = new Intent(Intent.ACTION_VIEW);
// 由于没有在Activity环境下启动Activity,设置下面的标签
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Android 7.0以上要使用FileProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
File file = new File(apk_filepath);
//参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致 参数3 共享的文件
Uri apkUri = FileProvider.getUriForFile(getContext(), getContext().getPackageName()+".fileprovider", file);
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(new File(apk_filepath)), "application/vnd.android.package-archive");
}
mContext.get().startActivity(intent);
}
当然还可以进行下载进度监听
/**
* 通过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;
}
使用
ContentObserver就行监听
/**
* 监听下载进度
*/
private class DownloadChangeObserver extends ContentObserver {
/**
* Creates a content observer.
*
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
public DownloadChangeObserver(Handler handler) {
super(handler);
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
}
/**
* 当所监听的Uri发生改变时,就会回调此方法
*
* @param selfChange 此值意义不大, 一般情况下该回调值false
*/
@Override
public void onChange(boolean selfChange) {
scheduledExecutorService.scheduleAtFixedRate(progressRunnable,0,2,TimeUnit.SECONDS);
}
}
/**
* 注册ContentObserver
*/
private void registerContentObserver() {
/** observer download change **/
if (downloadObserver != null) {
mContext.get().getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), false, downloadObserver);
}
}
/**
* 注销ContentObserver
*/
private void unregisterContentObserver() {
if (downloadObserver != null) {
mContext.get().getContentResolver().unregisterContentObserver(downloadObserver);
}
}
使用scheduledExecutorService进行定时任务刷新
/**
* 关闭定时器,线程等操作
*/
private void close() {
if (scheduledExecutorService != null && !scheduledExecutorService.isShutdown()) {
scheduledExecutorService.shutdown();
}
if (downLoadHandler != null) {
downLoadHandler.removeCallbacksAndMessages(null);
}
}
重点代码已经贴上