ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Android包管理服务Pkms:核心机制、关键流程与排查实践

Android包管理服务Pkms:核心机制、关键流程与排查实践 如果你在 Android 上碰到过一个应用怎么都装不上、装上后权限又乱套、或者开机后某个预置应用“离奇消失”的问题折腾到最后你八成会撞上同一个名字——Pkms。Pkms 是 PackageManagerService 的缩写Android Framework 里最核心的系统服务之一掌管着从开机扫描 APK、解析 AndroidManifest.xml、安装卸载、权限授予到 Intent 路由和包信息广播的整套体系。这篇内容我按 AOSP 源码主线把 Pkms 从头到尾拆一遍重点放在它究竟做了什么、关键流程长什么样、出了问题怎么查适合应用开发、系统开发、以及想深入 Framework 的读者。1. 先搞清 Pkms 的定位它不是“装 App 的工具”那么简单很多人对包管理服务的第一反应就是“负责装 App 的”。这个理解不能说错但严重低估了它。Pkms 在 Android 系统里承担的角色有点像整个操作系统的“户籍管理处 工商登记处 市场监管总局”三个部门合体。它不光管新应用怎么进来还得管应用的身份、权限、组件、存储位置、运行状态甚至连广告推送能不能弹窗都跟它有关系。1.1 从 SystemServer 启动序列看 Pkms 的江湖地位Android 系统的 Java 层服务全部由 system_server 进程拉起。SystemServer 把所有服务分成三批启动引导服务、核心服务、其他服务。Pkms 属于第一批引导服务范畴在 startOtherServices 方法中被启动启动时机非常早早到几乎所有需要“知道系统里装了哪些应用”的服务都得排队等它。startBootstrapServices() - startInstallService() // 安装服务底层依赖 installd - startPackageManagerService() // Pkms 在这里登场Pkms 初始化完之后它立刻向 ServiceManager 注册自己的 Binder 服务名像package、package_native这些。从此以后任何进程想查询包信息、安装卸载应用、申请权限都要通过 Binder 调用到 Pkms 所在的 system_server 进程。有一点值得注意Pkms 初始化会先读已保存的包状态也就是/data/system/packages.xml这类文件然后才开始扫描各个预置 APK 目录和应用安装目录。这意味着每次开机时系统都会重建一遍“应用清单”。这也是为什么你把 APK 丢进/system/app后要重启才生效——因为 Pkms 只在开机扫描时才发现它它可不会时时刻刻盯着目录看。1.2 Pkms 的职责全景一个列表看清边界我用一个表格来梳理 Pkms 管辖的范畴。这个表格建议你收藏排查问题时先对照一下方向就容易找到了。职责分类具体内容典型的对外接口包扫描与解析开机扫描 APK 目录解析 AndroidManifest.xmlscanDirTracedLI / parseBaseApk安装与卸载安装、更新、卸载应用创建应用数据目录installStage / deletePackage包信息查询查询应用列表、包名、版本、签名、权限getPackageInfo / queryIntentActivities组件解析根据 Intent 找到匹配的 Activity / Service / ProviderresolveActivity / queryIntentServices权限管理权限的定义、授予、撤销、检查grantRuntimePermission / checkPermission包状态管理开机完成、停止状态、默认应用、包禁用/启用setApplicationEnabledSetting存储管理APK 存放位置、数据目录、卸载残留清理reconcileSdkData / freeStorage广播派发安装、卸载、替换后对外发广播通知sendPackageBroadcast这里我想特别强调一下组件解析。Intent 匹配是非常高频的操作你在应用里写的startActivity最终都会走到 Pkms 去查“这个 Intent 到底应该打开谁”。所以 Pkms 不光是装应用的它是整个应用之间协作与调度的中枢。如果某个隐式 Intent 突然打不开任何应用常见原因之一就是 Pkms 侧的解析结果异常。2. 开机扫描与包解析Pkms 如何“看见”每一个 APKPkms 第一次和某个应用打交道绝大多数情况发生在开机扫描阶段。这段逻辑藏在 PackageManagerService 构造函数里核心方法是 scanDirLI / scanPackageLI。整个流程我们可以分成三步来看扫描目录、解析 APK、落盘保存。2.1 扫描目录哪些路径会被纳入包管理范围Pkms 并不是把整个文件系统翻一遍找 APK它只扫描几个固定目录这些目录在 Android 分区规范里写死了。常见扫描路径包括/system/priv-app系统特权应用比如 Settings、SystemUI拥有特权权限/system/app普通系统应用/vendor/app、/product/app厂商定制应用/data/app用户安装的应用以包名-随机后缀的子目录形式存放/system/overlay运行时资源覆盖包RRO扫描时 Pkms 会对每个子目录里的base.apk做解析。如果遇到同一包名同时出现在多个目录会按优先级处理系统目录里的预置应用优先于/data/app里的同名应用但/data/app里如果有更新版本那么更新版本会被设为当前生效版本系统 APK 本体则作为“回滚目标”保留。这里有个我在实际项目中踩过的坑厂商在/system/priv-app里放了一个 APK但忘了设置android:directBootAware结果设备第一次解锁前应用数据一直不可访问表现就是开机后应用白屏。这类问题在日志里很难直接看到最后是靠dumpsys package核对包状态才定位出来的。2.2 APK 解析AndroidManifest.xml 不是给人读的APK 本质上是个 zip 包但里面的 AndroidManifest.xml 是二进制编码过的不能直接当文本读。Pkms 会调用包解析器Android 12 之后是 ParsingPackageUtils老版本叫 PackageParser把二进制 XML 解码提取应用包名、版本号、SDK 版本、权限声明、四大组件、Intent Filter、签名等信息填充为一个完整的 Package 对象。ParsingPackageUtils.parseBaseApk(ParseType, File, ...) - parseBaseApkCommon() - 解析 manifest 属性package、versionCode、minSdk、targetSdk - 解析 uses-permission、permission、permission-tree - 解析 application 下所有 activity / service / receiver / provider - 解析组件里的 intent-filter构建 IntentFilter 对象 - 收集 instrumentation、sharedUserId、签名、包可见性配置解析的时候Pkms 会校验很多约束。比如包名不能为空、版本号必须为正、声明的组件名称必须合法、targetSdk 不能高于当前平台版本太多高版本会有 compatibility 逻辑。有一次我在做 CTS 认证时遇到一个第三方预置应用把android:versionCode写成了字符串解析器直接抛出异常那个应用开机就被踢出扫描列表。这也是为什么说APK 的 manifest 一旦写错应用连进系统名单的机会都没有。2.3 落盘与缓存为什么重启后包信息还在扫描解析是一个非常耗时的过程每个包都要做一次二进制 XML 解析。如果每次开机都全量重新解析所有 APK那系统启动时间会非常难看。Pkms 的优化方案有两层第一层把已经解析出来的包信息保存在/data/system/packages.xml。下次开机时 Pkms 先加载这个文件建立基础的包信息再扫描目录做差异化更新。这就像你昨天整理好的通讯录今天只需要更新新加的联系人而不是从头翻名片。第二层Dex 优化产物如oat/vdex文件也会缓存避免每次开机重新编译。开机扫描时 Pkms 会根据 APK 的修改时间和 dexopt 状态判断是否需要重新优化。packages.xml文件本身是 XML 格式记录着每个包的名称、版本、uid、签名、权限授予情况。如果这个文件损坏轻则 Pkms 把部分包信息打回出厂状态重则直接触发系统进入“优化应用”的恢复流程。我自己遇到过一次设备意外断电后 packages.xml 发生写坏的情况开机后系统发现包信息和实际目录对不上走了 Android 的PackageManagerException恢复逻辑把一批应用标记为“未安装”。所以重要数据一定要及时备份这句老话在 Framework 层面同样成立。3. 安装 APK 的全链路从 adb install 到 Pkms 落盘如果说开机扫描是 Pkms 被动“认识”应用那安装流程就是它主动“收编”应用。这条链路非常长从你在电脑上敲adb install到最后桌面出现新图标中间要经过 Shell、PackageInstaller、Pkms、installd 好几个层次。我把这条链路由外到内拆开讲。3.1 请求入口PackageInstaller 与 Session 机制现代 Android 的安装流程对外统一暴露的是PackageInstaller接口。无论是 adb、应用商店还是系统设置最终都是通过创建安装会话Session来提交安装任务的。Session 机制的意义在于APK 往往有好几百 MB不可能一次性塞给 Binder 传输所以它允许你开一个“会话”往里面分段写入 APK 数据最后再 commit。adb install app.apk - shell 进程调用 PackageInstaller.createSession() - Pkms 创建一个 PackageInstallerSession分配 sessionId - shell 通过 session.write() 把 APK 数据写入 staging 目录 - shell 调用 session.commit() - PackageInstallerSession 进入提交阶段真正启动安装Session 的 staging 文件存放在/data/app-staging目录。这个路径很容易在排查存储不足问题时被忽视——系统盘空间告急时即使你有几百 MB 用户空间staging 也可能写不进去安装就报INSTALL_FAILED_INSUFFICIENT_STORAGE。这类问题只看机型剩余存储根本看不出来一定要同时查看/data分区整体使用率。3.2 commit 之后预检、快照与安装事务commit 是安装流程的分水岭。从这一步开始所有操作被纳入一个类事务机制要么成功落盘要么清理干净不会留一个半残的包。提交后 Pkms 会做一系列预检校验安装包的签名是否合法、是否需要签名轮换检查当前是否有同包名应用决定是全新安装还是覆盖更新检查版本号是否满足要求默认禁止降级校验请求安装的 uid 是否有安装权限校验目标 SDK 版本和设备配置的兼容性预检不通过直接返回错误码比如INSTALL_FAILED_VERSION_DOWNGRADE、INSTALL_FAILED_UPDATE_INCOMPATIBLE就发生在这个阶段。前者好理解版本号低了后者要解释一下它往往是因为现有应用和新安装包的签名不一致、或者现有包声明了 sharedUserId 但新包没声明导致 Pkms 认为这两个包不是同一个“身份”拒绝覆盖。预检通过后APK 会被复制到最终安装目录通常是/data/app/packageName-suffix/。接着 Pkms 再次解析这个 APK拿到完整的 Package 信息然后调用 installd 创建应用的数据目录。数据目录就是后面运行时context.getDataDir()指向的路径每个用户一个位于/data/user/userId/packageName/下。3.3 installd 与 native 层建目录、优化 Dex、校准 SELinuxPkms 是 Java 层服务但它很多脏活累活是通过 Binder 交给 native 层的 installd 去做的。名字就叫 install daemon跑在init进程下的一个守护进程。它负责的操作包括创建和删除 app 数据目录设置目录的 SELinux 标签和 uid/gid执行 dex2oat 做 dex 优化管理b/目录下的 native bridge 等特殊情况dex2oat 是这里面的重头戏。它在 Android 5.0 的 ART 时代被引入作用是把 APK 里的 dex 字节码编译优化成 oat 文件提高应用运行速度。安装时 Pkms 会根据包的配置选择编译过滤器比如默认很多设备走的是“speed-profile”模式——只对应用热方法做 AOT 编译其余走 JIT既保证启动速度又控制安装耗时。我记得有个 OEM 项目当时为了优化首次开机时间把系统预置应用全部改成 verify 模式只校验不编译。结果是开机时间压下去了但应用实际运行时的冷启动性能明显下降尤其是微博、淘宝这类大应用快慢差距肉眼可见。后来他们改成 speed-profile才在开机时间和用户体验之间找到平衡。这种取舍没有标准答案完全取决于产品定位。3.4 安装完成的收尾工作安装落盘之后并不算完。Pkms 还需要做三件收尾的事一是更新内存里和packages.xml里的包信息把新包装进mPackages这个核心 HashMap。二是根据 manifest 声明的权限对所有“安装即授予”的权限做授权并更新运行期权限状态。三是广播通知其他组件发出ACTION_PACKAGE_ADDED/ACTION_PACKAGE_REPLACED。桌面就是靠监听这类广播来刷新应用图标的。这里有个细节经常被应用层开发忽略覆盖安装时ACTION_PACKAGE_REPLACED只会发给被替换的应用自己其他应用如果想在应用被替换时得到通知应该监听ACTION_PACKAGE_ADDED里携带的EXTRA_REPLACING标志。我见过不止一个 SDK 因为这个细节漏掉替换事件导致登录态没清、推送通道白名单没更新。这就是 Framework 行为与直觉不一致的典型例子。4. 核心数据结构与状态流转看懂 Pkms 的“记忆”Pkms 内部维护着大量包相关数据理解这些数据结构你就基本能读懂dumpsys package输出的东西了也能在出问题时瞬间判断是哪个字段异常。4.1 Package 与 PackageSetting一个面向运行时一个面向持久化Pkms 里有两套“包对象”一套是Package或 Android 13 之后的AndroidPackage它是 APK 解析后的纯内存对象包含 manifest 里所有信息只读唯一的用途是给系统和应用提供查询。另一套是PackageSetting它是包在设备上的状态记录包含安装位置、uid、版本、enabled/stopped/suspended 标志、默认活动、权限授予情况等。这套数据会序列化到 disk也就是 packages.xml 和/data/system/users/userId/runtime-permissions.xml。两者的关系你可以类比成“身份证信息”和“户籍档案”前者记录你是谁、姓名、出生地后者记录你住哪里、有没有被限制出行、有没有领过补助。Pkms 每次重启后都要先把户籍档案读回来才允许你去查身份证信息。4.2 几个关键的状态位与它们的影响dumpsys package输出里经常会看到几个状态字段我挑影响大且容易踩坑的来说状态字段含义典型异常表现stopped应用处于停止状态无法接收广播尤其是静态注册的广播suspended应用被暂停如企业策略图标变灰点击无反应hidden应用被隐藏桌面和系统设置列表里消失instantInstant App 标志应用只能持有临时授权清理后消失stopped状态值得单独吐槽一下。Android 有个规则如果应用处于 Force-stop 或停止状态系统会屏蔽它的静态广播接收器。这在保护用户隐私和资源上是有意为之但很多开发者不知道应用在后台被系统或用户清理之后推送消息就再也收不到了查手机会发现服务还在跑其实只是广播被 Pkms 拦截了。如果你负责的应用是强推送依赖这几乎是必修课。4.3 dumpsys package 实战怎么读输出定位问题排查任何包相关的问题我第一件事永远是拉 dumpsys。下面这个命令的输出信息量很大adb shell dumpsys package packageName关键段落包括Packages:包的基本信息包括版本、uid、targetSdk、codePathActivity Resolver Table:已注册 Activity 的信息和匹配规则Runtime Permissions:运行时权限授予状态User 0:下面的installedtrue/stoppedfalse/suspendedfalse举个例子有次一个应用启动黑屏查日志没发现 crash。我拉 dumpsys 一看targetSdk33但设备已经升到 Android 14应用没有正确适配android:exported属性Activity 在 Package 解析时就被拒收进组件表。dumpsys 里Activity Resolver Table中死活查不到这个 Activity问题一目了然。这就是 dumpsys 的价值它把 Pkms 的最终判定结果直接摆给你看不用去猜。5. 权限管理与组件解析Pkms 的另一半江山包信息管理只是 Pkms 一半的职责另一半是权限管理和 Intent 路由。这块也是日常开发遇到最多“莫名奇妙”问题的地方。5.1 权限授予的几个关键时机Android 6.0 之后的运行时权限模型授权逻辑的最终裁判就是 Pkms。权限授予有几个关键时机安装时对签名权限、特权权限、普通权限做一次性授权运行时应用通过requestPermissions弹窗PermissionController 收集用户选择后回调 Pkms 写进运行时权限文件覆盖安装时如果包 targetSdk 升级部分权限可能需要重新申请权限检查链条是应用调用context.checkSelfPermission()- AMS / Binder - Pkms.checkPermission()。Pkms 会根据 uid、权限名、目标包名、用户 id 做多级校验包括权限是否定义、调用者是否持有、权限保护级别是否匹配、运行时权限是否被用户拒绝。我在代码里还提过一句checkPermission返回PERMISSION_GRANTED不代表当前应用真的能用这个权限因为持有权限的一方可能因为设备政策DevicePolicyManager被临时吊销。真机生产环境里这类“间接吊销”最容易被忽略排查时记得也要看appops状态。5.2 Intent 路由resolveActivity 的匹配逻辑startActivity之所以能找到目标 Activity靠的是 Pkms 的 resolveIntent 和 queryIntentActivities。匹配时 Pkms 要过好几道关卡Action 必须匹配Category 必须全部匹配DataUri、MIME type必须匹配Component 显式指定时优先隐式匹配时看 Intent-Filter根据包可见性规则过滤targetSdk 30 且未声明queries的应用默认看不到其他应用的组件最后这条是 Android 11 之后的一个大坑。包可见性规则出台后很多老应用在 targetSdk 抬升后突然 “startActivity 找不到 Activity”因为 Pkms 在匹配前先做了可见性过滤直接把没声明queries的第三方包过滤掉了。报错很奇怪明明装了应用resolveActivity却返回 null。排查这类问题就看一个地方manifest 里缺了queries声明。5.3 包变更广播Pkms 如何通知整个系统应用安装、卸载、替换、状态变化后Pkms 都会发出广播。这套广播机制是系统里所有“对包变化敏感”的组件保持同步的基础。典型广播包括ACTION_PACKAGE_ADDEDACTION_PACKAGE_REMOVEDACTION_PACKAGE_REPLACEDACTION_PACKAGE_CHANGEDACTION_PACKAGE_DATA_CLEARED广播发出时Pkms 会附带包名、uid、是否替换等 Extra。注册动态广播Context.registerReceiver才能稳定收到静态注册的接收器在 Android 8.0 之后对大部分隐式广播都失效了所以我现在做系统级监控首选是动态注册再配合 DeviceOwner 权限。顺便说一个常被用到的技巧如果你想监听所有应用的安装行为做合规审计可以注册ACTION_PACKAGE_ADDED但注意它和ACTION_PACKAGE_FIRST_LAUNCH的触发时机完全不同。前者是安装完就发后者是用户首次点开 App 才发。有一次我们做首次启动引导统计就直接接错了广播导致数据偏高一倍。6. 踩坑实录Pkms 常见问题与排查手段这部分把我这些年实际遇到的高频问题整理出来每条都给出根因、现象和排查方法。有些是从系统侧踩的坑有些是应用侧踩的坑但根子都在 Pkms 这一层。6.1 安装失败错误码和你想象的往往不一样INSTALL_FAILED_UPDATE_INCOMPATIBLE这个错误码很常见但很多人不知道它到底在说什么。它的意思是Pkms 找到了一个包名相同的已有应用但新旧包之间的某些“身份属性”不匹配导致无法执行覆盖更新。最常见的三种原因签名不一致老包是 A 签名新包是 B 签名Pkms 判定包归属发生变更拒绝覆盖。sharedUserId 不一致老包声明了 sharedUserId新包没有声明或者声明了不同的 sharedUserId。已安装应用是系统应用但当前是普通安装上下文。第二种原因尤其隐蔽。有一次我们升级预置 SDK 相关包失败日志里就是这个错误查了半天签名没问题最后发现是老版本误声明了 sharedUserId。解决办法是把老包先卸载或确认不再使用 sharedUserId 后重装。还有一种现象是INSTALL_FAILED_NO_MATCHING_ABIS翻译过来是“没有匹配的 CPU 架构”。多发生在 APK 里只包含armeabi-v7a的 so 库但设备是纯 64 位或者只包含x86而设备是 ARM。排查时用 unzip 看 APK 里的lib/目录结构即可。6.2 packages.xml 异常当 Pkms 的“户籍档案”坏了packages.xml 不是普通文件它是 Pkms 每次安装、卸载、授权时都会异步刷新写入的关键元数据。如果设备在写文件时断电这个文件可能处于半损坏状态导致重启后 Pkms 读档失败。典型现象是开机后一堆应用显示为“未安装”或者明明/data/app下面有 APKPkms 却找不到。此时系统的行为是把 packages.xml 视为无效并从已有目录重建包数据库这个重建过程会付出代价所有应用的权限授予需要重新校验、部分应用被标记为重启后需要重新优化。应对策略按严重程度先备份现有 packages.xml哪怕是坏的里面可能还有部分可读信息对比 packages.xml 里的包列表和 /data/app 下的实际目录找出差异差异大的直接尝试进入 recovery 恢复出厂差异小的可以擦除 packages.xml 后等系统重建但坦白讲出现 packages.xml 损坏往往意味着设备在非正常断电、存储故障等恶劣条件下运行。最重要的还是做低层防护确保vold/文件系统层面有掉电保护否则 Framework 层再怎么修复都是亡羊补牢。6.3 dexopt 缓存问题改包名后“旧优化文件”还会捣乱Android 的 ART 编译产物 oat/vdex 文件存放在应用目录下的oat/子目录里。出现“代码改了但运行还是老逻辑”的情况大多数都指向 dexopt 缓存未失效。要记得Pkms 在决定是否重新 dexopt 时会看 APK 的修改时间、文件大小、odex 文件是否存在等指标。如果这些 checksum 没变化它会直接复用旧编译产物。当你用一个同名 APK大小相同、时间相同替换内容时系统可能认为“没变”继续用旧缓存表现就是改了代码但行为还是旧的。强制重新编译的命令adb shell cmd package compile -m speed -f packageName-f就是 force 重新编译。如果是系统开发阶段还可以直接清掉/data/app/pkg/oat/目录下文件再重启。这个问题在应用侧也可能遇到——用 adb 覆盖安装时如果 APK 的 build 时间戳没变、大小完全相同其实也存在缓存命中的风险。处理方法是 build 时保证 ZipAlign / 签名后文件大小变化或者卸载重装。6.4 预置应用特权权限白名单校验系统预置应用如果声明了一些signature|privileged保护级别权限Pkms 会校验它们是否在特权白名单里。白名单文件路径在/etc/permissions/privapp-permissions-*.xml。如果应用声明了一个特权权限但没进白名单Pkms 会把它记录为“权限未授予”更严重的版本里系统会在启动时直接拒绝加载该应用导致预置 App 形同虚设。日志里常看到PackageManager: Privileged permission ... for package ... not in privapp-permissions whitelist遇到这个不用怀疑就是白名单漏配了。你把权限加进对应产品目录的 privapp-permissions XML 文件、推到设备、重启即可。这个问题在厂商定制 Rom 阶段出现频率极高几乎每周都能碰到一次。7. 性能观察与调优让 Pkms 跑得更快Pkms 的代码量非常大AOSP 里 PackageManagerService.java 是最庞大的几个类之一。但它实际对性能的敏感点其实集中在少数几条路径上开机扫描、dexopt、广播风暴。掌握这几个点你就知道该从哪下手优化设备整体启动和安装体验。7.1 开机扫描为什么慢主要瓶颈在解析和校验开机时 Pkms 要扫描的目录里可能躺着几百个预置应用。每个应用都要做一次 APK 解析解析本身耗时不大真正的重头是 dexopt 决策和 odex 文件校验。Android 8.0 前后的“开机优化应用”进度条就是系统在批量 dexopt。改善方向有几个预置应用尽量在编译产物阶段就准备好oat文件开机只做校验不做编译条件允许时将部分预置包延迟到开机后空闲时间再优化清理不必要的系统应用几百个预置包里往往有一大半是定制运营商渠道包我在某个运营商项目里做过一次统计预置应用超过 120 个其中约 40% 是几乎没人使用的渠道应用。砍掉一半之后首次开机时间缩短了近 30 秒。这个优化思路在 Framework 层面很简单只需要调整产品分区里的 APK 列表但收益立竿见影。7.2 dexopt 策略的取舍编译过滤器选项很多最常见的排列是 verify、quicken、speed-profile、speed、everything。它们的性能与编译耗时呈正比。选哪个取决于设备定位低端机适合速度优先开机时间敏感应用运行性能可略让步中高端机适合 speed-profile兼顾安装速度和实际运行流畅度极速启动设备可对冷门应用全部 verify挑高频应用单独 speed真正要在代码层调优可以关注DexManager、PackageDexOptimizer里的策略特别是 boost 场景如应用商店批量更新时。在批量更新流程里Pkms 允许降低编译等级以提升安装吞吐这个逻辑在DefaultDexoptManager里可配。7.3 系统升级与多用户场景下的 Pkms每次 Android OTA 升级后你会看到“正在优化应用”那是 Pkms 在版本变更后重新做 dexopt。因为系统版本变化可能导致编译完成的 odex 不兼容。多用户场景下Pkms 还要为每个用户维护一份包状态例如工作资料里的应用和主用户的应用安装状态相互独立一个用户卸载不影响另一个用户这些都是 Pkms 内部状态机的体现。从研究角度我给你的建议是沿着PackageManagerService.java中的installStage-installPackageTracedLI-scanPackageLI这条主线读几遍配合dumpsys package输出对照看。不要一开始就扎进 IPC 细节和 Binder 模板代码那是劝退主力。先把主干流程画出一道来每看到一个环节就想想“如果这里出错了实际表现是什么”有了这个习惯你已经比大多数只会用 adb install 的开发者强太多了。最后分享一个我自己常用的观察手段在 AOSP 或厂商代码里打开PackageManagerService的 verbose 日志配合 logcat 过滤PackageManager、PackageInstaller、installd三个 tag装一个 APK 就能看到从 session 创建到安装完成的完整链路。刚开始你会觉得输出量很大等你把每个 log 节点和上面讲的流程对应上之后这个日志就是你排查安装问题的底牌。
返回列表