Android13-包安装器PackageInstaller-之apk安装流程

目的

  • 我们最终是为了搞明白安装的整个流程
  • 通过安卓系统自带的包安装器来了解PMS 安装流程
  • 实现需求定制:静默安装-安装界面定制-安装拦截验证。【核心目的】

安装流程和PMS了解不用多说了; 安装定制相关:

  • 手机上安装时候弹出锁屏界面需要输入密码;
  • 安装时候弹出密码框,让用户输入定制的特殊密码功能;
  • 安装页面客制化需求

安装方式

当然正常的安装分为类型我其实理解为大概3种

  • 无界面安装:PMS启动阶段 比如系统第一次启动,所有内置app自动批量安装;我们重试系统app开发时候,或者内置系统apk开发时候,删除对应的目录下的apk和apk对应的/data/分区下的apk所有安装信息后,push
    更新的apk到系统,重启。 apk 自动重新安装。

  • adb 安装: adb 命令安装,通过adb install 安装,依托守护进程来实现安装

  • 点击安装或者调用方法安装:应用市场再下载完apk后自动进入进入包管理器进行安装;sd开或者外部存储中的安装包点击安装自动进入包管理器进行安装

相关资料推荐;

承接上文,P

<think>好的,我现在需要帮助用户解决在Android中使用PackageInstaller安装Split APK时遇到的commit()方法抛出的IllegalArgumentException问题,特别是关于PendingIntent的可变标志(mutable PendingIntent)的问题。用户还提到之前的引用内容,我需要结合这些信息,但可能用户提供的引用并不完全相关,所以我需要主要依赖自己的知识和之前的问题回答来构建解决方案。 首先,我需要回顾一下用户的问题:用户在使用PackageInstaller安装Split APK时,遇到了IllegalArgumentException,错误信息提到“The commit() status receiver should come from a mutable PendingIntent”。这表明在调用commit()方法时,传递给PackageInstaller的PendingIntent没有正确设置为可变的(FLAG_MUTABLE),或者存在其他相关配置错误。 接下来,我需要回忆AndroidPackageInstaller的工作流程,特别是安装Split APK的部分。Split APKAndroid应用捆绑的一种形式,允许将应用拆分成多个APK文件,通常用于动态功能模块。安装这类APK需要使用PackageInstaller API,其中涉及到创建安装会话、写入APK文件、提交会话等步骤。提交会话时,需要提供一个PendingIntent来接收安装结果的状态更新。 根据之前的回答,用户的问题可能出在PendingIntent的创建上。从Android 12(API 31)开始,创建PendingIntent时必须显式指定FLAG_MUTABLE或FLAG_IMMUTABLE标志。如果未指定,或者错误地使用了不可变的标志,而实际需要修改PendingIntent中的Intent内容,就会导致IllegalArgumentException。 现在,我需要将这一知识应用到Split APK安装场景中。在PackageInstaller中,当提交安装会话时,必须传递一个PendingIntent,该PendingIntent指向一个BroadcastReceiver,用于接收安装结果。如果这个PendingIntent没有正确设置为可变的,系统可能无法更新其中的Intent extras,从而导致异常。 接下来,我需要考虑Split APK安装的具体步骤,并检查每个步骤中PendingIntent的配置是否正确。可能的错误点括: 1. 创建PendingIntent时未添加FLAG_MUTABLE标志。 2. 在Android 14及以上版本中,使用了隐式Intent而没有正确添加FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT标志。 3. 没有正确设置Intent的显式目标组件,导致系统认为Intent是隐式的。 此外,还需要确保在安装Split APK时,所有相关的APK文件都被正确写入到安装会话中,并且会话的提交参数正确。 然后,我需要整理出具体的解决步骤,可能括: - 确保PendingIntent使用FLAG_MUTABLE标志。 -Android 14+中处理隐式Intent的限制。 - 验证Intent是否为显式,并正确设置目标组件。 - 确保安装会话的所有APK文件都被正确写入。 - 处理兼容性问题,特别是不同Android版本的差异。 同时,需要参考用户提供的引用内容,其中第一条提到在PackageInstaller中使用PendingIntent.getBroadcast时,组合使用了FLAG_UPDATE_CURRENT和FLAG_MUTABLE,这可能是一个正确的示例。用户可能在此处需要类似的代码配置。 最后,我需要确保回答结构清晰,分步骤说明问题原因和解决方案,并提供代码示例和注意事项,帮助用户理解和实施修复。</think>### 分步解决 Split APK 安装的 `IllegalArgumentException` 问题 在 Android 中使用 `PackageInstaller` 安装 Split APK(即多 APK 安装)时,若未正确配置 `PendingIntent` 的可变性标志,或会话参数错误,会导致 `commit()` 方法抛出 `IllegalArgumentException`。以下是详细解决方案: --- #### **一、问题根本原因** 1. **可变性标志缺失** - 从 **Android 12(API 31)** 开始,所有 `PendingIntent` 必须显式指定 `FLAG_MUTABLE`(可变)或 `FLAG_IMMUTABLE`(不可变)标志[^1]。若未声明,系统会直接抛出异常。 - **Split APK 安装场景特殊性**:安装会话的 `commit()` 方法需要通过 `PendingIntent` 动态更新安装状态(如进度、错误信息),因此必须使用 `FLAG_MUTABLE`。 2. **隐式 Intent 限制(Android 14+)** - **Android 14(API 34)** 要求:若 `PendingIntent` 使用 `FLAG_MUTABLE` 且关联的 `Intent` 是隐式的(未明确指定目标组件),必须添加 `FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT` 标志,否则触发异常[^2]。 --- #### **二、Split APK 安装完整流程与修复代码** **步骤 1:创建安装会话并配置参数** ```kotlin val packageInstaller = context.packageManager.packageInstaller val sessionParams = PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL ).apply { setAppPackageName(packageName) // 目标应用名(若更新) setInstallReason(PackageManager.INSTALL_REASON_USER) } val sessionId = packageInstaller.createSession(sessionParams) val session = packageInstaller.openSession(sessionId) ``` **步骤 2:写入 Split APK 文件到会话** ```kotlin splitApkFiles.forEach { apkFile -> val inputStream = FileInputStream(apkFile) val outputStream = session.openWrite("split_${apkFile.name}", 0, apkFile.length()) inputStream.copyTo(outputStream) session.fsync(outputStream) outputStream.close() inputStream.close() } ``` **步骤 3:创建 `PendingIntent` 并提交会话** - **关键修复点**:正确配置 `FLAG_MUTABLE` 和显式 Intent。 ```kotlin // 显式指定接收安装状态的 BroadcastReceiver val intent = Intent(context, InstallResultReceiver::class.java).apply { action = "ACTION_INSTALL_COMPLETE" putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId) } // 创建可变 PendingIntent(必须 FLAG_MUTABLE) val pendingIntent = PendingIntent.getBroadcast( context, sessionId, // 使用唯一 requestCode(如 sessionId) intent, PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) // 提交安装会话 session.commit(pendingIntent.intentSender) session.close() ``` **步骤 4:处理 Android 14+ 的隐式 Intent 限制** 若需动态传递额外参数(如 `EXTRA_STATUS`),需显式定义 `BroadcastReceiver`: ```kotlin // 显式指定目标 Receiver 类 val intent = Intent(context, InstallResultReceiver::class.java).apply { action = "ACTION_INSTALL_COMPLETE" putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId) } // 或添加安全标志(仅在必须隐式时) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { val flags = PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT pendingIntent = PendingIntent.getBroadcast(context, sessionId, intent, flags) } ``` --- #### **三、关键注意事项** 1. **会话生命周期管理** - 确保在 `commit()` 前关闭所有 `OutputStream`,否则安装会卡在 `STATUS_PENDING_USER_ACTION`。 - 通过 `PackageInstaller.SessionCallback` 监听会话状态。 2. **接收安装结果** - 实现 `BroadcastReceiver` 处理结果: ```kotlin class InstallResultReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1) val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) // 处理安装成功/失败逻辑 } } ``` 3. **Android 版本兼容性** ```kotlin // 动态选择标志 val flags = when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> PendingIntent.FLAG_MUTABLE else -> PendingIntent.FLAG_UPDATE_CURRENT // 旧版本可能无需显式标志 } ``` --- #### **四、错误场景排查表** | 错误现象 | 可能原因 | 解决方案 | |-------------------------------------------|-----------------------------------|------------------------------------------| | `commit()` 抛出 `IllegalArgumentException` | `PendingIntent` 未设置 `FLAG_MUTABLE` | 添加 `FLAG_MUTABLE` 标志 | | 安装状态未回调 | Intent 未显式指定 Receiver 类 | 使用显式 Intent 或添加安全标志(Android 14+) | | 安装进度卡在 0% | 未正确写入 APK 文件到会话 | 检查文件流是否关闭,确认 `session.fsync()` 调用 | --- #### **五、扩展优化建议** 1. **会话重试机制** 若安装失败(如用户取消),通过 `PackageInstaller.getSessionInfo(sessionId)` 查询状态并重试。 2. **用户权限处理** - 检查 `Manifest.permission.INSTALL_PACKAGES` 权限(系统应用需要)。 - 处理 `STATUS_PENDING_USER_ACTION` 状态,引导用户确认安装。 3. **性能优化** 使用 `ExecutorService` 并行写入多个 Split APK 文件,加快安装速度。 [^1]: Android 12 及以上版本强制要求 `PendingIntent` 的可变性声明,详见 [官方文档](https://developer.android.com/reference/android/app/PendingIntent)。 [^2]: Android 14 对隐式 Intent 的限制说明,参考 [Android 14 行为变更](https://developer.android.com/about/versions/14/behavior-changes-all#implicit-intents)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值