运行环境
- Android 5.1.1, API 22
解决方案
下载新版本的
apk
public void downloadNewVersion() {
mDownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
// apkDownloadUrl 是 apk 的下载地址
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkDownloadUrl));
// 获取下载队列 id
enqueueId = mDownloadManager.enqueue(request);
}注册接收下载完成的广播
BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
long downloadCompletedId = intent.getLongExtra(
DownloadManager.EXTRA_DOWNLOAD_ID, 0);
// 检查是否是自己的下载队列 id, 有可能是其他应用的
if (enqueueId != downloadCompletedId) {
return;
}
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(enqueueId);
Cursor c = mDownloadManager.query(query);
if (c.moveToFirst()) {
int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
// 下载失败也会返回这个广播,所以要判断下是否真的下载成功
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
// 获取下载好的 apk 路径
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
// 提示用户安装
promptInstall(Uri.parse("file://" + uriString));
}
}
}
};
// 注册广播, 设置只接受下载完成的广播
registerReceiver(receiver, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));取消注册广播, 不取消注册的话, 调用
recreate
时会报Are you missing a call to unregisterReceiver()?
错误
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mBroadcastReceiver);
}提示用户安装
private void promptInstall(Uri data) {
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(data, "application/vnd.android.package-archive");
// FLAG_ACTIVITY_NEW_TASK 可以保证安装成功时可以正常打开 app
promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(promptInstall);
}就是这样,运行 app 看下效果吧
总结
坑比较多,花了3天时间才完全实现这个效果
1个坑是必须使用setDataAndType()
,而不能单独使用setData()
和setType()
另外一个坑是getUriForDownloadedFile
现在返回的是content://
这种格式的链接了,无法用于启动intent
最后一个坑是intent
必须设置FLAG_ACTIVITY_NEW_TASK
, 否则安装完成后无法正常打开app
常见问题
提示解析程序包时出现问题
答
mDownloadManager.getUriForDownloadedFile(enqueueId)
返回的Uri
是这种格式的content://downloads/my_downloads/83
, 这种格式的Uri
启动intent
会报解析程序包时出现问题
No Activity found to handle Intent
No Activity found to handle Intent { act=android.intent.action.VIEW dat=/storage/sdcard/download/demo.apk typ=application/vnd.android.package-archive
答
可能原因1:
有可能是没有权限读取这个文件或者文件路径错误
可能原因2:
// Intent promptInstall = new Intent(Intent.ACTION_VIEW)
// .setData(data)
// .setType("application/vnd.android.package-archive");
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(data, "application/vnd.android.package-archive");
单独设置data
和type
也会造成这个错误,原因请看http://developer.android.com/reference/android/content/Intent.html#setData(android.net.Uri))
你会发现无论何时你去设置data
或者type
, 另外一个都会自动的变为空, 例如: setData()
会使得type
参数值为空, 如果你想让两者都生效的话只能使用setDataAndType()
提示应用程序未安装
答
有可能是因为APK
签名不一致, 比如之前是debug
版(无签名), 现在你更新安装release
版(有签名), 就会出现这个问题
参考链接
- Android 安卓系统提示应用程序未安装的解决方法
- Android DownloadManager and Media Scanner
- Android: install .apk programmatically
- 为何必须用setDataAndType, 而不能单独设置data和type
- Android DownloadManager Example
- 在应用中更新App版本
- DownloadManager补漏
- 全局接收下载完成的广播
更新纪录
- 2016年3月8日 添加取消注册广播的代码
- 2016年1月22日 发布