Android7.0以上系统无法跳转到APK安装页

本文介绍了解决Android设备上安装APK时遇到的跳转安装页面问题的方法,包括添加必要的权限、定义FileProvider及指定文件路径等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先表明原文地址,这几行代码都一样,随便找了一篇https://www.jianshu.com/p/9fdd84274954

但是!我的问题是按照上述文章处理后,不报FileUriExposedException了  ,但依然不跳转安装页面,也不报错,也不跳转,毫无头绪,很头疼,不知道问题在哪,所以这是最恶心的地方。

我是加上了一个安装权限就好了

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

因为7.0以上的手机都会有一个“未知来源应用权限”的开关,默认是不允许的,加上这个之后,就会自动跳转到这个开关页面,打开就好了,不打开还是不会跳到安装页面

好了,下面说一下通用的步骤:

1, 定义FileProvider

<manifest>
    ...
    <application>
        ...
   
         <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.test.CrossBorderSellPlatform.fileprovider"//自定义名字 为避免重复建议设为:包名.fileprovider
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
        ...
    </application>
</manifest>

 

2,指定可用的文件路径  (我是这么写的   试过别人的  但是对我好像不太好使)

在项目的res目录下,创建xml文件夹,并新建一个provider_paths.xml文件。通过这个文件来指定文件路径。

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="files_root"
        path="." />
    <root-path
        name="root_path"
        path="." />
</paths>

3, 开始跳转

/**
     * @param file
     * @return
     * @Description 安装apk
     */
    protected void installApk(File file) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
            Uri apkUri = FileProvider.getUriForFile(context, "com.test.CrossBorderSellPlatform.fileprovider", file);  //包名.fileprovider
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }

4.如果这三步完成后,你那里跟我一样不好使,就在清单文件把最上面那个权限加上

 

纠结了我两天,网上找的办法都是前三步那个样子,总算是完事了,今晚不用加班了,鼓掌!!!

<think>我们正在讨论Android U(API级别33)及以上版本使用PackageInstaller安装APK的方法。 根据引用[2]和引用[3]的内容,在Android 7.0(API 24)及以上版本,由于文件权限的更改,我们不能再使用`file://`URI来传递APK文件,而应该使用FileProvider来生成`content://`URI。 但是,在Android U(API 33)及以上版本,Google进一步加强了隐私和安全,对于安装APK的方式也有新的要求。特别是,从Android 11(API 30)开始,我们可能需要处理包可见性(package visibility)的问题,而在Android U中,可能还有新的变化。 然而,引用[2]中提到了使用PackageInstallerActivity的方法,但在较新的Android版本中,直接启动系统安装界面可能不再推荐,因为系统组件可能会变化,而且从Android 10开始,Google限制了应用启动后台活动。 实际上,在Android U及以上,我们更推荐使用PackageInstaller API来安装APK,这是一个更系统、更安全的方式。PackageInstaller允许我们以静默方式安装应用(需要系统权限)或者创建一个安装会话,然后通过一个意图让用户确认安装。 但是请注意,普通应用无法使用PackageInstaller进行静默安装(需要系统签名或root权限)。普通应用通常只能启动系统安装界面(使用Intent.ACTION_INSTALL_PACKAGE或Intent.ACTION_VIEW),或者使用PackageInstaller创建一个安装会话并启动确认界面。 下面我将介绍在Android U及以上版本使用PackageInstaller安装APK的步骤: 1. 创建安装会话:使用PackageInstaller创建一个新的安装会话。 2.APK文件写入会话。 3. 提交会话,并获取一个PendingIntent用于启动安装确认界面。 4. 启动安装确认界面,用户确认后安装。 注意:在Android U(API 33)及以上,我们还需要注意以下几点: - 需要申请`REQUEST_INSTALL_PACKAGES`权限(在Android 8.0及以上需要)。 - 如果目标SDK版本为Android 11(API 30)或更高,需要在清单文件中声明需要交互的包(使用<queries>元素)或者使用package visibility API。 - 使用FileProvider提供文件内容URI,确保文件共享安全。 但是,使用PackageInstaller API进行安装(非静默,即需要用户确认)并不需要特殊权限(除了`REQUEST_INSTALL_PACKAGES`),因为最终用户会看到安装界面并确认。 步骤详解: 步骤1:在AndroidManifest.xml中添加必要的权限和FileProvider配置。 添加权限: ```xml <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> ``` 配置FileProvider(如果还没有): ```xml <application ...> ... <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> ... </application> ``` 在res/xml目录下创建file_paths.xml(例如): ```xml <?xml version="1.0" encoding="utf-8"?> <paths> <!-- 这里配置你存放APK路径 --> <external-path name="external_files" path="." /> </paths> ``` 步骤2:使用PackageInstaller进行安装。 以下是一个示例方法,用于在Android U及以上版本安装APK: ```java private void installApk(Context context, File apkFile) { // 检查是否具有安装权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!context.getPackageManager().canRequestPackageInstalls()) { // 如果没有权限,则跳转到设置界面申请权限 Uri packageURI = Uri.parse("package:" + context.getPackageName()); Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); return; } } PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.setSize(apkFile.length()); int sessionId = -1; try { // 创建会话 sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); // 将APK文件写入会话 InputStream in = new FileInputStream(apkFile); OutputStream out = session.openWrite("base.apk", 0, apkFile.length()); byte[] buffer = new byte[65536]; int length; while ((length = in.read(buffer)) != -1) { out.write(buffer, 0, length); } session.fsync(out); out.close(); in.close(); // 创建用于启动安装确认界面的PendingIntent Intent intent = new Intent(context, InstallReceiver.class); // 需要一个广播接收器来接收安装结果 intent.setAction("com.example.INSTALL_COMPLETE"); // 自定义action PendingIntent pendingIntent = PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); // 提交会话,准备安装 session.commit(pendingIntent.getIntentSender()); session.close(); } catch (IOException | SecurityException e) { e.printStackTrace(); if (sessionId != -1) { packageInstaller.abandonSession(sessionId); } } } ``` 步骤3:创建一个广播接收器(InstallReceiver)来接收安装结果。 ```java public class InstallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent != null) { int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); String message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); switch (status) { case PackageInstaller.STATUS_PENDING_USER_ACTION: // 需要用户确认,这里会启动一个Activity Intent activityIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT); context.startActivity(activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); break; case PackageInstaller.STATUS_SUCCESS: // 安装成功 Toast.makeText(context, "安装成功", Toast.LENGTH_SHORT).show(); break; case PackageInstaller.STATUS_FAILURE: case PackageInstaller.STATUS_FAILURE_ABORTED: case PackageInstaller.STATUS_FAILURE_BLOCKED: case PackageInstaller.STATUS_FAILURE_CONFLICT: case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: case PackageInstaller.STATUS_FAILURE_INVALID: case PackageInstaller.STATUS_FAILURE_STORAGE: // 安装失败 Toast.makeText(context, "安装失败: " + message, Toast.LENGTH_SHORT).show(); break; } } } } ``` 注意:在清单文件中注册广播接收器: ```xml <receiver android:name=".InstallReceiver" android:exported="true"> <intent-filter> <action android:name="com.example.INSTALL_COMPLETE" /> </intent-filter> </receiver> ``` 但是请注意,以上代码中,我们创建了一个广播接收器来接收安装结果。当安装需要用户确认时,我们会收到`STATUS_PENDING_USER_ACTION`状态,然后启动一个Activity(即系统安装界面)让用户确认。 然而,在Android U及以上版本,Google可能对安装流程有进一步调整,因此建议测试不同版本。 另外,上述方法在写入APK文件时使用了流操作,需要注意文件读取权限和存储权限(在Android 10及以上,可能需要使用MediaStore或SAF)。 总结:在Android U及以上,使用PackageInstaller API进行安装是一个更现代的方式,它避免了直接调用系统组件的风险(因为系统组件可能变化)。同时,我们还需要处理权限请求和文件访问问题。 注意:以上代码仅为示例,实际应用中需要处理更多的异常和兼容性问题。 引用[1]提到,这个过程虽然涉及到一些权限和API的调用,但只要按照步骤来,实现起来并不复杂。希望这篇文章能帮助你顺利地完成这个任务。 如果你在实现过程中遇到任何问题,欢迎随时向我咨询。祝你编程愉快![^1] 另外,根据引用[3],我们还需要注意在AndroidManifest.xml中添加`REQUEST_INSTALL_PACKAGES`权限,并且确保文件存在。 最后,关于在Android U及以上版本,可能还需要考虑其他新的安全限制,比如存储访问的变化(例如,Scoped Storage)等。因此,确保你的应用正确处理了APK文件的存储位置和访问方式。 如果您的应用需要支持更广泛的Android版本,可能需要同时处理旧版本(使用Intent安装)和新版本(使用PackageInstaller)的安装方式。但根据问题,我们只关注Android U及以上版本。 希望这个教程对您有帮助!</think>### Android U及以上版本使用PackageInstaller安装APK教程 在Android U(API 33)及以上版本中,由于系统安全策略升级,安装APK需要采用新的PackageInstaller API。以下是完整实现步骤: #### 1. 添加权限和配置 ```xml <!-- AndroidManifest.xml --> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application> <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> </application> ``` #### 2. 创建文件路径配置 `res/xml/file_paths.xml`: ```xml <paths> <external-path name="external_storage" path="." /> </paths> ``` #### 3. 核心安装代码(支持Android U+) ```java public void installApk(Context context, File apkFile) { // 检查安装权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !context.getPackageManager().canRequestPackageInstalls()) { Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES) .setData(Uri.parse("package:" + context.getPackageName())); context.startActivity(intent); return; } // 创建安装会话 PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL ); try { int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); // 写入APK数据 try (InputStream in = new FileInputStream(apkFile); OutputStream out = session.openWrite("base.apk", 0, -1)) { byte[] buffer = new byte[16384]; int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } session.fsync(out); } // 创建安装确认Intent Intent intent = new Intent(context, InstallResultReceiver.class); intent.setAction("INSTALL_COMPLETE_ACTION"); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE ); // 提交安装会话 session.commit(pendingIntent.getIntentSender()); session.close(); } catch (IOException e) { e.printStackTrace(); } } ``` #### 4. 安装结果接收器 ```java public class InstallResultReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1); switch (status) { case PackageInstaller.STATUS_PENDING_USER_ACTION: // 触发用户确认界面 Intent confirmationIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT); context.startActivity(confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); break; case PackageInstaller.STATUS_SUCCESS: Toast.makeText(context, "安装成功", Toast.LENGTH_SHORT).show(); break; default: String errorMsg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); Toast.makeText(context, "安装失败: " + errorMsg, Toast.LENGTH_LONG).show(); } } } ``` #### 5. 注册广播接收器 ```xml <receiver android:name=".InstallResultReceiver" android:exported="true"> <intent-filter> <action android:name="INSTALL_COMPLETE_ACTION" /> </intent-filter> </receiver> ``` ### 关键注意事项 1. **文件访问权限**:Android U+要求使用FileProvider访问APK文件 2. **动态权限**:必须动态申请`REQUEST_INSTALL_PACKAGES`权限 3. **会话管理**:每个安装都需要独立会话ID 4. **结果处理**:必须通过广播接收器处理安装结果 5. **Android 14+适配**:需添加`<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>`[^3] 此方法符合Android U+的安全规范,避免了直接调用系统组件的兼容性问题,能稳定触发系统原生安装界面[^2]。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值