概要
在线应用内更新 在APP开发中是最基础的一项功能。主要可以分为以下几步:
正文
这里略过1—4步骤,主要讲下载及安装部分。
文件的下载有很多中方式与第三方框架,这里由于仅仅是下载应用,所有综合考虑,强烈建议使用安卓 SDK 自带下载框架 DownloadManager 。
1.最基础也是重要的 申请文件读写权限、网络权限,8.0系统需要获取“未知应用安装权限”
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
2.检测版本与安装权限
由于 8.0 “未知应用安装权限”被除掉,替换为“未知来源应用安装权限”,默认是关闭状态,需要用户手动开启才能安装最新版本安装包,所以这里需要判断是否为 8.0 系统,然后获取是否有该权限。
为了用户体验比较好,这里我在 onResume 中进行了版本判断。其中 haveInstallPermission 为 boolean 类型,默认为true。
//版本判断
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//获取是否有安装未知来源应用的权限
haveInstallPermission = MainActivity.this.getPackageManager().canRequestPackageInstalls();
}
3.封装下载请求
这里由于注释很详细,就不多做解释,直接上代码。关于 DownloadManager.Request 的属性详解可以查看这里
public void downloadAPK() {
//已存在 -- 删除
File apkFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxxx.apk");
if (apkFile != null && apkFile.exists()) {
apkFile.delete();
}
//下载 (AppCont.Update_URL 是你的app下载地址)
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(AppCont.Update_URL));
//设置title
request.setTitle(GetResourcesUtil.getString(R.string.app_name));
// 完成后显示通知栏
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
//设置存储路径 --这个是应用专用的,软件卸载后,下载的文件将随着卸载全部被删除
request.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, "xxx.apk");
//设置 文件类型
request.setMimeType("application/vnd.android.package-archive");
downloadManager.enqueue(request);
}
4.开始下载
在第二步中拿到了 是否有安装未知来源应用的权限 ,可以在用户点击确定下载最新版本时去判断,如果没有权限,跳转到权限授权界面;如果有权限,则直接下载更新。
if (!haveInstallPermission) {
ToastUtils.showShort("请打开安装未知来源应用的权限");
//没有权限 在 yourAppPackageName 设置你的app包名
Uri packageURI = Uri.parse("package:" + yourAppPackageName);
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
MainActivity.this.startActivityForResult(intent, 010);
}else {
diaglog.dismiss();
ToastUtils.showShort("正在下载....");
//下载更新
updateUtil = new UpdateUtil(MainActivity.this);
updateUtil.downloadAPK();
}
5.设置广播接收器,监听下载完成
downloadmanager 在下载完成时,会发送一条广播,我们可以设置接收器,接受这条广播,并做下载完成后的操作。
这里先做广播接收器
public class DownloadManagerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//下载完成
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
//获取 downloadmanager 下载任务id
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
//安装应用
UpdateUtil updateUtil = new UpdateUtil(context);
updateUtil.installApk(id);
}
}
}
// AndroidManifest.xml
"com.xxxx.xxxx.util.DownloadManagerReceiver">
"android.intent.action.DOWNLOAD_COMPLETE"/>
在更新提示的界面注册广播接收器。建议在 onResume() 中注册,在onPause() 中解除注册。
@Override
protected void onResume() {
super.onResume();
//注册广播
downloadManagerReceiver = new DownloadManagerReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
registerReceiver(downloadManagerReceiver, intentFilter);
}
}
@Override
protected void onPause() {
super.onPause();
//解除注册
unregisterReceiver(downloadManagerReceiver);
}
6.安装app
在安装 app 的时候需要获取 apk 存储地址,这里需要注意一下,需要做6.0以下(不包含6.0)/6.0/7.0以上(包含7.0)三种版本的适配。
6.0以下与6.0系统的区别 与 适配:
6.0以下(不包含6.0)系统的可以直接获取存储路径;getUriForDownloadedFile得到 值为: content://downloads/my_downloads/10
Android6.0以下,getUriForDownloadedFile得到的值为:file:///storage/emulated/0/Android/data/packgeName/files/Download/xxx.apk7.0适配 :
由于7.0对文件访问权限做出了限制,必须通过 FileProvider 封装后访问。具体详解可以查看这里
public Uri getDownloadPath(long downloadId) {
Uri path = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//6.0 以下
path = downloadManager.getUriForDownloadedFile(downloadId);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
//6.0 — 7.0
path = Uri.fromFile(queryDownloadedApk(downloadId));
} else {
//7.0 以上
path = FileProvider.getUriForFile(context, "com.xxx.xxxx", new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxxx.apk"));
}
return path;
}
private File queryDownloadedApk(long downloadId) {
File downloadFilePath = null;
DownloadManager.Query query = new DownloadManager.Query();
Cursor cursor = null;
try {
cursor = downloadManager.query(query.setFilterById(downloadId));
if (cursor != null || cursor.moveToFirst()) {
String uriString = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (!TextUtils.isEmpty(uriString)) {
downloadFilePath = new File(Uri.parse(uriString).getPath());
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return downloadFilePath;
}
获取到apk存储路径后,可以进行最后的步骤安装了,这里很简单,直接上代码。
public void installApk(long id) {
//在上面已经获取到apk存储路径
Uri uri = getDownloadPath(id);
//跳转安装
Intent intentInstall = new Intent();
intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentInstall.setAction(Intent.ACTION_VIEW);
intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
context.startActivity(intentInstall);
}
感谢以下博客提供的思路与学习机会:
系统自带DownloadManager详解
Android app 在线更新那点事儿(适配Android6.0、7.0、8.0)
Android系统内置下载器服务DownloadManager的使用