最近Google发布8.0加强了未知应用的监管,本文记录解决8.0App版本自动更新由于没有未知应用安装权限(此权限需用户手动开启)导致升级失败问题。兼容8.0以前的版本。温馨提示记得添加基本权限(6.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" />
1、首先在项目的res->values目录下新建一个file_paths.xml文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path
name="download"
path="" />
</paths>
</resources>
2、接下来在AndroidManifest.xml->application下添加如下内容,其中FileProvider这里不解释,请Google或百度:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
3、以下内容的前提是安装包已经下载完毕后的操作,检查是否获得未知应用安装权限(Android8.0系统):
注:1、showError()方法是自定义toast,修改为你自己提示方式的就可以了;
2、DialogHelper.showConfirm()为自定义提示框,同样修改为你自己的提示对话框;
3、对应字符串说明:
apk_damage:{0}安装包已损坏或不存在;
app_name:应用名称;
updateFile:apk文件路径;
need_unknown_app_permission:安装应用需要打开未知来源权限,请去设置中开启权限
/**
* 检测是否已经打开权限
*
* @param updateFile 安装包文件
*/
private void checkNeedPermission(File updateFile) {
if (updateFile == null) {
showError(MessageFormat.format(getString(R.string.apk_damage), getString(R.string.app_name)));
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean haveInstallPermission = getPackageManager().canRequestPackageInstalls();
if (haveInstallPermission) {
startInstallAPK(updateFile);
} else {
this.updateFile = updateFile;
DialogHelper.showConfirm(getString(R.string.need_unknown_app_permission), new DialogFactory.OnDialogListener() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onConfirm() {
startInstallPermissionSettingActivity();
}
@Override
public void onCancel() {
}
});
}
} else {
startInstallAPK(updateFile);
}
}
4、打开未知应用界面,用户处理后会通过onActivityResult返回处理结果:
private static final int INSTALL_PACKAGES_REQUEST_CODE = 0x001;
unknownApkInstallDenied字段我这里是用来判断用户拒绝开启未知应用权限后再次安装时不用重新下载apk直接进行第3步操作使用,如无相关业务逻辑删除便是。
/**
* 打开未知应用界面
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity() {
Uri packageURI = Uri.parse("package:" + getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
startActivityForResult(intent, INSTALL_PACKAGES_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == INSTALL_PACKAGES_REQUEST_CODE) {
this.unknownApkInstallDenied = false;
startInstallAPK(updateFile);
} else {
this.unknownApkInstallDenied = true;
}
}
5、开始安装最新apk:
Uri contentUri = FileProvider.getUriForFile(context, "你的应用包名.fileprovider", updateFile);
中的“你的应用包名.fileprovider”对应步骤2中
android:authorities="${applicationId}.fileprovider"
(注:${applicationId}默认就是项目包名,前提是你没有手动修改过app->build.gradle->defaultConfig下的applicationId)必须一致
/**
* 开始安装
*
* @param updateFile 安装包文件
*/
private void startInstallAPK(File updateFile) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, "你的应用包名.fileprovider", updateFile);
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(FileUtils.getFileExtension(updateFile));
intent.setDataAndType(contentUri, type);
} else {
String type = "application/vnd.android.package-archive";
intent.setDataAndType(Uri.fromFile(updateFile), type);
}
startActivity(intent);
finish();
}
/**
* 获取全路径中的文件拓展名
*
* @param file 文件
* @return 文件拓展名
*/
public static String getFileExtension(File file) {
if (file == null) return null;
return getFileExtension(file.getPath());
}