如何在Android Oreo(8.0)中管理未知来源应用安装?
刚好之前踩过Android 8.0未知来源安装适配的坑,整理了完整的用户侧变化和开发者适配流程,希望能帮到你:
Android Oreo(8.0)未知来源应用安装的核心变化与开发者适配指南
一、用户视角的安装方式变化
Android 8.0对「未知来源应用」的安装逻辑做了根本性调整:
- 8.0之前:只需全局开启系统设置中的「未知来源」开关,就能安装所有第三方APK
- 8.0及以后:移除全局开关,改为针对单个应用授予安装权限——用户安装某应用的APK时,会被引导到系统设置页面,给该应用单独开启「允许来自此来源的应用」权限,授权后才能完成安装
二、开发者视角的完整权限获取与安装流程
这部分是适配的核心,步骤如下:
1. 在Manifest中声明必要权限
首先需要添加安装权限的声明:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
2. 检查当前应用是否已拥有安装权限
在触发安装前,先判断应用是否具备安装未知应用的权限,根据结果分支处理:
// 定义请求码 private static final int REQUEST_CODE_INSTALL_PERMISSION = 1001; private void checkInstallPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean hasPermission = getPackageManager().canRequestPackageInstalls(); if (!hasPermission) { // 无权限,跳转到系统设置页面申请 Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_CODE_INSTALL_PERMISSION); } else { // 已有权限,直接执行安装逻辑 startInstallApk(); } } else { // 8.0以下版本,直接执行安装(前提是用户已开启全局未知来源) startInstallApk(); } }
3. 处理权限申请的回调结果
在onActivityResult中接收用户的授权结果(如果使用Jetpack的Activity Result API,写法会更简洁):
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_INSTALL_PERMISSION) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean hasPermission = getPackageManager().canRequestPackageInstalls(); if (hasPermission) { // 用户授权成功,启动安装 startInstallApk(); } else { // 用户拒绝授权,给出提示 Toast.makeText(this, "未授予安装权限,无法完成应用安装", Toast.LENGTH_SHORT).show(); } } } }
4. 执行APK安装的通用逻辑
完成权限校验后,就可以执行安装操作,注意适配7.0+的FileProvider要求:
private void startInstallApk() { // 示例:获取下载目录下的APK文件 File apkFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "target_app.apk"); Intent installIntent = new Intent(Intent.ACTION_VIEW); installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0及以上需要用FileProvider获取URI Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", apkFile); installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive"); } else { installIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); } startActivity(installIntent); }
补充:FileProvider的配置(7.0+必备)
在Manifest中添加FileProvider声明:
<provider android:name="androidx.core.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>
然后在res/xml目录下创建file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 对应上述代码中的下载目录 --> <external-files-path name="download" path="Download/" /> </paths>
如果开发者未做以上适配,在8.0+设备上直接触发安装会导致失败,甚至抛出异常,所以务必按流程完成权限申请与适配。
内容的提问来源于stack exchange,提问作者Patneu




