PackageManagerService深度解析:Android包管理与APK安装全流程
2026/9/9 8:13:50 网站建设 项目流程

搞客户端开发久了的人,多少都遇到过“应用装不上”或者“刚装上就秒闪退”的怪事。查了一圈网络、缓存、存储空间,最后发现真正的原因往往藏在系统层:所有跟APK安装、卸载、升级、权限授予、组件注册相关的操作,最终都会汇聚到 system_server 进程里的一个服务——PackageManagerService,简称 PMS。它管着包信息的“总账本”,也管着安装和管理的全流程执行,算是Android系统里最核心、也最容易让人一头雾水的服务之一。

这篇文章我打算按自己实战排查时的思路来写,把PMS的启动链路、APK安装的完整流程、包管理涉及的数据结构和状态变化,以及日常定位问题时会用的经验都串一遍。不看官方文档散落的细节,也不逐行贴代码,而是讲清楚“它为什么这么设计”和“遇到问题怎么用它”,适合刚转系统开发的工程师,也适合想搞懂安装失败原理的应用层同学。

1. PMS到底在系统里扮演什么角色

1.1 一句话理解PackageManagerService

PackageManagerService并不是一个独立进程,它是运行在 system_server 进程里的一个 Binder 服务,对外通过 IPackageManager 接口给所有应用提供能力。应用层调用的getPackageManager(),底层握住的其实就是这棵Binder树的远端代理。

你可以把它理解成酒店的“前台+档案室”。客人要订房、退房、改房、查房,都得找前台;前台背后有一大堆纸质档案记录每个房间住过谁、什么时候登记的、门禁卡权限开到哪一层。对Android系统来说,每个APK就是一个“客人”,PMS负责登记客人信息、安排房间(UID和数据目录)、发门禁卡(权限和签名校验),还要在客人退房时把档案销掉。

1.2 它负责的几类核心事务

把PMS的活拆开看,主要是下面几块,互相之间有很强的关联性:

  • 元信息管理:记录每个已安装包的包名、版本号、UID/GID、签名证书、代码路径、data目录、安装位置等。
  • 组件注册:把APK解析出来的 Activity、Service、Receiver、Provider 和 Instrumentation 等组件登记到内部表,供 ActivityManager、Launcher、startActivity 等查询。
  • 安装与卸载:主担全量安装、覆盖升级、卸载、清除数据、安装会话管理。
  • 权限管理:处理权限申请记录、签名权限校验、运行时权限授予和撤销,以及默认权限规则。
  • Intent解析:queryIntentActivities()resolveActivity()这类接口的最终执行者,决定一个隐式Intent会命中哪个组件。
  • 与installd协同:创建/删除应用数据目录、处理DEX优化,依赖的是一个叫 installd 的native守护进程。

这套职责很重的系统,设计上并非把每个细节都自己做完,而是更像一个“调度中枢”。比如创建目录这种底层操作,PMS自己不碰文件系统,而是通知 installd 去做,原因后面会讲到。

1.3 PMS不在独立进程里,这才是关键

很多刚接触系统源码的同学会有一瞬间的困惑:PMS这么大一个服务,怎么没有自己的独立进程?它跟 ActivityManagerService(AMS)、WindowManagerService(WMS)一样,全部住在 system_server 里。

这个设计有历史原因,也有性能考量。PMS跟AMS之间的交互非常频繁,比如启动Activity时要查包信息、要判断权限,如果拆成两个独立进程,每次都是一次完整Binder跨进程调用,系统关键路径上的延迟会高得离谱。放在同进程里,它们可以直接调用对方的方法,只是需要在锁上做好同步。代价也很明显:system_server是个“巨兽进程”,PMS一旦出现严重的死锁或内存问题,整个系统可能都会重启,而不是只挂掉一个服务。

理解这点之后,再看“为什么安装一个大App时系统偶尔会卡一下”就好理解了:PMS在扫描APK、解析Manifest、写packages.xml的时候,是拿着自己的全局锁的,这个锁会跟AMS的很多操作互相等待。系统整体出现瞬时卡顿,往往就是这些重量级操作在打架。

2. PMS的启动链路:开机时的“清点资产”

2.1 system_server里的启动入口

PMS不是在应用安装时才被创建的,而是开机时由 SystemServer 在startBootstrapServices()阶段启动。这一步非常重要,因为它是整个系统“包知识”的起点。启动入口是静态方法PackageManagerService.main(),传入几个关键参数:installer(连到installd的桥),factoryTest(是不是工厂测试模式),onlyCore(是否只加载核心包)。

main()方法内部会 new 出一个 PMS 实例,构造函数做完一大堆初始化后,再把自己注册到 ServiceManager,这样其他模块就能通过Binder拿到它。换句话说,系统进程起来之后,PMS是“先整理完自己家里的账本,才开门营业”的。

构造函数里干的事非常多,我按顺序挑重点讲:先初始化 Settings 对象,从/data/system/packages.xml/data/system/packages.list读入上次关机前的包状态;然后检查系统目录结构;接着逐个扫描系统分区和用户数据分区的APK,把解析结果注册进内存表;最后把权限同步一遍,再写回 packages.xml,保证本次扫描结果不丢。

2.2 扫描目录的顺序为什么不能乱

PMS在开机时会对以下几类目录做扫描,顺序很讲究:

  • /system/framework:框架级APK和共享库,最先加载。
  • /system/priv-app:特权系统应用,拥有普通系统应用没有的权限。
  • /system/app:普通系统应用。
  • /vendor/app/product/app/system_ext/app:厂商和产品分区应用。
  • /data/app:用户安装的应用目录。

顺序之所以不能乱,是因为系统应用要先注册好,才能给后扫描的包提供签名权限和共享库依赖。比如一个三方应用声明了某个签名权限,而这个权限是系统应用定义的,那系统应用必须先完成注册,三方应用的权限校验才能通过。

另外,factoryTest参数决定了是否扫描/data/app。工厂测试模式只加载核心系统包,不加载用户数据,这也是产线测试机器启动更快的原因之一。正常情况下,扫描完系统包,还会继续扫描用户分区,并对开机前没有完成的DEX优化任务做收尾。

2.3 首启与二次开机的差别

第一次开机和后面每次开机的开销差别巨大。首次开机/data分区是空的,PMS没有任何历史包袱,每个系统APK都要从头解析一遍Manifest,还要给大量框架包做DEX优化,所以首启慢是正常的。二次及以后的开机,PMS可以借助上次扫描生成的缓存和优化结果,把一部分工作省掉,但仍然会重新解析APK,并不是完全走“读缓存”这条路。

不少ROM团队在优化开机速度时,都会集中在PMS扫描这块做文章,比如增加并行扫描线程、调整DEX优化时机、延迟部分预装应用到后台再解析。我自己调过一台低端机,仅仅是把不必要的预装应用从/system/app挪到/data/app按需安装,开机时间就肉眼可见地快了不少。背后的道理就是:PMS扫描的包越少、优化任务越分散,开机压力就越小。

3. APK安装流程拆到最细

3.1 安装入口:PackageInstaller会话机制

说到安装,很多人第一反应是adb install,但走到系统内部,几乎所有安装入口都收敛到 PackageInstaller 这套会话机制上。它从Android 5.0开始引入,设计思路很像一个“事务”:先创建会话,再往里写APK数据,最后提交,提交失败可以整段回滚。

整个流程拆开是这样的:

  1. 安装器(比如应用商店,或者我们通过pm install触发的 shell 层)调用IPackageInstallercreateSession(),传入包名、安装模式、大小、安装位置等参数,得到一个 sessionId。
  2. 拿 sessionId 打开PackageInstaller.Session,通过它的 OutputStream 把APK内容写进去。此时文件只会落到一个临时暂存目录,通常是/data/app-staging/vmdlXXX.tmp
  3. 数据写完,安装器调用commit(),真正触发PMS做校验和安装。

这种设计的好处非常明显:大数据量的传输和PMS核心逻辑解耦,安装器可以先下载完整APK再提交;一个会话还能传入多个split APK;提交前可以随时放弃而不污染系统状态。对普通开发者来说,这个模型也解释了为什么“文件还没拷贝完就commit”会导致INSTALL_FAILED_SESSION_INVALID这类错误。

3.2 会话提交后发生了什么

commit()被调用后,PackageInstallerSession 会先做一次“安全检查”,确认会话里的文件真实存在、大小匹配、包名跟SessionParams一致。随后PMS会把这次提交包装成一个安装任务,交给内部的消息循环PackageHandler去排队处理。

PackageHandler 是PMS里面一个容易被人忽略的角色,它维护了一个安装请求队列。所有安装操作不会直接在当前线程执行,而是投递到INIT_COPY消息里排队。这样做的好处是安装请求可以被串行化,避免多个安装任务同时改/data/app导致文件状态错乱。在实际调试中,如果你同时adb install两个App,后一个往往会等前一个完成,这就是PackageHandler在排队。

任务轮到之后,handleStartCopy就上场了:把暂存目录里的APK文件拷贝到正式的/data/app/包名-一串随机后缀/目录下,同时解析一遍基础信息,校验签名,最后进入installPackageTracedLI这个核心方法。

3.3 解析APK:从二进制Manifest到对象

APK本质是一个ZIP包,AndroidManifest.xml是二进制XML格式,不能简单地用文本解析器读。PMS内部使用的解析器是 PackageParser,它专门负责把这套二进制格式读成人能看懂的包对象AndroidPackage

解析过程不是只读一个Manifest那么简单,它要处理非常多的字段:包名、版本号、minSdk/targetSdk、uses-permission、application下的各种组件、sharedUserId、installLocation、以及 split 分包配置等。遇到有 split APK 的应用,还要解析出 base APK 和各个 feature/config split,最后组合成一个完整的包视图。

这里有个经典坑:Manifest里如果写了系统不认识的新标签,旧版本Android往往会忽略;但如果是标签属性值非法,比如minSdkVersion不是整数,解析阶段就会直接抛异常,安装中断,返回INSTALL_PARSE_FAILED_MANIFEST_MALFORMED。所以当你看到一个安装错误带PARSE_FAILED字样,第一反应应该是:APK的Manifest文件本身有问题,而不是系统空间不够。

3.4 逐项校验:签名、ABI、版本、SharedUserId

解析出包信息后,真正的“审查环节”才开始,PMS会做一整套兼容性和安全性检查。我用实际项目里遇到的问题一个个说:

  • 签名校验:安装新包时,如果是覆盖旧版本,必须保证新旧签名一致,否则返回INSTALL_FAILED_UPDATE_INCOMPATIBLE。Android 7.0之后,系统优先使用 APK Signature Scheme v2/v3 的签名信息做整体校验,老应用没有v2签名才会回退到v1的JAR签名校验。targetSdk越高的应用,对签名方案的要求就越严格。
  • ABI检查:APK的lib/目录里如果有 native so,PMS会跟设备支持的ro.product.cpu.abi列表做匹配。旧设备经常出现“APK在测试机上能装,在真机上 INSTALL_FAILED_NO_MATCHING_ABIS”,基本都是so的ABI目录不匹配。
  • 版本检查:覆盖安装时,如果新版本号低于旧版本,默认会拒绝,除非显式带上INSTALL_ALLOW_DOWNGRADE标志。
  • SharedUserId冲突:APK声明了sharedUserId,如果跟已有包不是同一个签名,会直接被拒绝,因为同一个共享UID组里的包必须签名一致才能互相“信任”。
  • 存储空间和目录状态:检查/data分区可用空间、目标安装位置是否合法。空间不足时返回INSTALL_FAILED_INSUFFICIENT_STORAGE

这些检查看似零散,但设计上有个共同目标:在改动系统状态之前,把一切能拒绝的理由都找到。这样才能保证安装过程要么成功,要么保持系统原状,不会出现装到一半发现签名不对,留下半残文件的局面。

3.5 扫描与注册:组件正式“上户口”

校验通过后,PMS会调用scanPackageTracedLI()把包完整注册进系统。这一步是安装流程里最重的一环:

  1. 检查内存里是否已经有同名包,有的话走更新逻辑,没有就走新增逻辑。
  2. 目标ABI确定后,通过mInstaller创建或更新应用数据目录/data/user/0/包名/,设备保护存储则是/data/user_de/0/包名/
  3. 把解析出来的组件注册进mActivitiesmServicesmReceiversmProviders这些内部表,之后任何 Intent 查询都能命中它。
  4. 把包加进mPackages这个以包名为key的Map,同时在mSettings里生成或更新对应的PackageSetting
  5. 如果有 sharedUserId,还要归并到共享用户组,统一分配 UID。
  6. 更新权限记录,把Manifest里声明的权限、gid等同步给 PermissionManagerService。
  7. 处理DEX优化任务,performDexOpt()或通过后台任务执行 dex2oat。
  8. 最后把mSettings写回/data/system/packages.xml,发广播通知系统内外:这个包已经可用了。

很多开发者以为“安装成功”就是文件被拷贝了,其实PMS视角里的成功是整套索引全部更新完毕。文件拷贝只是前菜,组件注册、权限绑定、数据目录创建、持久化、广播通知,每一步不到位,应用都算不上真正“安装好了”。

3.6 installd:真正落盘的那个家伙

前面好几次提到 installd,这里单独展开。既然PMS是系统进程,权限已经很高了,为什么创建目录这种事还要麻烦另一个守护进程?答案是SELinux和用户数据隔离。

/data/user/0/包名这种目录不是随便建一个文件夹就行,它需要按照system_appuntrusted_appplatform_app等不同的SELinux domain设置标签,还要设置正确的UID、GID、目录权限。这些涉及内核级安全的操作,如果都放到 system_server 里做,等于把高风险的文件系统操作暴露在核心进程里,出问题就是大问题。

installd 是一个独立的native守护进程,以root身份运行,通过Binder接收 PMS 发来的命令,执行createAppDatarestoreconDataclearAppDatadexopt等底层操作。PMS只负责“决定”要不要建目录、建在哪个用户下,installd 负责“执行”并保证结果符合安全策略。这个分层也是Android“系统服务只逻辑处理,底层操作用单独进程”这一思想的典型代表。

4. 包管理:安装之外的“改状态”功夫

4.1 PMS的核心数据结构

做PMS相关开发,最需要先认识的是这几个内存数据结构:

  • mPackagesArrayMap<String, AndroidPackage>,包名到包解析结果的映射,是所有包信息查询的第一站。
  • mSettings:持久化配置载体,管着所有包的状态,包括安装路径、版本、disabled状态、共享UID组等。PMS每次关键变更后都会把mSettings写盘。
  • PackageSetting:对应一个已安装包,是“这次安装留下的档案卡”。里面包括codePathresourcePathpkgFlagsuidversionCodesignaturesenabledSetting(是否被禁用)、suspend(是否被冻结)等。
  • mActivities/mServices/mReceivers/mProviders:组件索引表,按组件名查Activity、Service、Receiver、Provider是它干的事。

这些结构之间的关系可以理解为:mPackages是“活的包信息”,mSettings是“落盘的包档案”,组件索引表是“对外提供检索的目录”。排查问题时,如果dumpsys里包还在但组件查不到,大概率是组件注册表出问题,这不常见,但一旦发生,表现就是“应用设置里能看到包,Launcher却找不到图标,startActivity也报错”。

4.2 packages.xml与packages.list

/data/system/packages.xml是PMS持久化状态的心脏。里面记录了每个包的代码路径、版本号、UID、签名摘要、权限列表、enabled状态等。PMS每次安装、卸载、更新、授权都会更新这个文件。注意它是先写备份文件packages-backup.xml,再替换主文件,防止写一半断电导致文件损坏。如果你在调试中手动改坏了这个文件,轻则所有包状态丢失,重则系统起不来。

/data/system/packages.list则偏向给native层使用,内容更精简:一行一个包,字段包括包名、UID、GID、SELinux label、data目录等。installd、netd这些native守护进程都以它为重要输入。

实操建议:在排查“重启后包信息不对”这种问题前,先备份packages.xml,改错了好恢复。另外不要用文本编辑器直接乱改,格式稍微不对,PMS解析失败就会回退到备份甚至重置状态。

4.3 升级、卸载、清除数据对状态的影响

同样是对包做操作,完整安装、覆盖升级、卸载对PMS状态的影响完全不同:

  • 完整安装:新增UID,新增PackageSetting,文件进入/data/apppackages.xml增加一条记录。
  • 覆盖升级:PackageSetting里的版本号和codePath会被更新,但UID和data目录保持不变,所以应用数据能保留下来。这也是为什么“升级不上数据丢失”的诉求通常要靠备份机制,而不是靠PMS。
  • 卸载:PMS从mPackages里移除Package对象,从mSettings里移除PackageSetting,调用 installd 删除用户数据目录。但pm uninstall -k-k参数时,会保留数据目录,只删代码和注册信息。
  • 清除数据:pm clear或设置里的“清除存储”,不会移除包注册信息,只清空/data/user/0/包名下的数据,并重置权限状态。

我在实际项目里遇到过一种场景:某应用被卸载后重新安装,竟然还能看到旧数据。排查下来是安装时调了pm uninstall -k,虽然界面显示应用没了,但数据目录还在。对普通用户这可能是个隐私隐患,对系统集成商这就是一个明确的产品规则:是否需要持久化数据,要在卸载流程里显式决定,不能把“卸载”和“清数据”完全划等号。

4.4 禁用与停用:不是删除的“软卸载”

PMS还支持一种“半卸载”状态:禁用。pm disable-user --user 0 com.xxx可以把某个应用置为DISABLED_USER,表现是从Launcher消失、无法启动,但包还留在系统里。对应的还有pm enable恢复。

这里有个容易混淆的点:DISABLED状态分好几种,包括DISABLED_UNTIL_USED。后者是系统为了“预装应用首次被使用后再启用”设计的状态,常见于一些默认关闭的系统组件。查看包当前状态可以直接dumpsys package com.xxx,里面有一行enabled=字段。

禁用不等于卸载,这是排查问题时特别值得记住的:如果一个预装应用“不见了”,先用pm list packages -d看看是不是被禁用,而不是直接怀疑安装失败。

4.5 权限授予与安装的关系

APK安装完成后,Manifest里声明的权限会被解析进权限系统。从Android 6.0开始,危险权限不再在安装时全部授予,而是运行时由用户决定。但这不代表PMS就完全不管了:普通权限会在安装时自动授予,签名权限会根据安装者签名和系统签名是否匹配做自动判定,并被记录在权限管理服务里。

安装流程中,grantPermissions那一步会遍历APK声明的权限,逐个决定是“默认授予”还是“等待运行时申请”。对于预装应用,系统还可能通过default-permissions.xml预授权一些敏感权限。很多“首启就要定位权限,不授权连默认页都进不去”的现象,其实都是目标Sdk版本和权限模型共同决定的。

调试时,adb shell pm grant 包名 权限名pm revoke 包名 权限名是绕开弹窗直接改权限状态的好工具。改完后重启会失效,但排查权限问题非常快。

5. 日常排查:错误码速查与实战经验

5.1 dumpsys package的正确打开方式

adb shell dumpsys package是PMS体检的第一入口,但直接输出非常长,学会缩小范围比会看全量更有用。

  • dumpsys package com.xxx:只看指定包的信息,包含安装用户、data目录、版本、signing keys、请求的权限等。
  • dumpsys package packages:只看包名列表和基础标识,适合确认包是否被识别。
  • dumpsys package permissions:查看权限授予状态,排查“这个权限到底给没给”。
  • dumpsys package dexopt:查看DEX优化状态,能发现“包还在但odex没生成”一类的问题。
  • dumpsys package installer:查看当前是否有残留的安装会话。

我通常的流程是:先pm list packages | grep 包名确认包是否在,再用dumpsys package 包名看细节,最后根据错误码决定要不要继续查 logcat。大部分安装问题在dumpsys package的输出里就露馅了。

5.2 常见安装错误码对照表

下面是我整理的高频错误码,按我遇到的频率排了序:

错误码含义常见触发原因
INSTALL_FAILED_UPDATE_INCOMPATIBLE覆盖更新不兼容新旧包签名不一致、组件冲突
INSTALL_FAILED_ALREADY_EXISTS包已存在安装了同名包且未带替换标志
INSTALL_FAILED_SIGNATURE_MISMATCH签名不匹配同一个包名用不同证书签两次
INSTALL_FAILED_VERSION_DOWNGRADE版本降级新版本号低于已安装版本
INSTALL_FAILED_NO_MATCHING_ABIS没有匹配的ABIAPK只含armeabi,设备是纯arm64
INSTALL_FAILED_INSUFFICIENT_STORAGE存储空间不足/data分区满或inode耗尽
INSTALL_FAILED_DEXOPTDEX优化失败dex2oat异常、ART缓存损坏
INSTALL_PARSE_FAILED_MANIFEST_MALFORMEDManifest解析失败APK的Manifest二进制格式损坏
INSTALL_FAILED_INVALID_APKAPK无效文件损坏、不是合法ZIP
INSTALL_FAILED_USER_RESTRICTED用户受限多用户策略禁止该用户安装
INSTALL_FAILED_SESSION_INVALID安装会话无效commit前数据未写完或会话过期

保存这张表很有用。看到错误码先定位到“哪一类”,再去查具体原因,比直接翻源码高效得多。

5.3 几个实战排查案例

我随手分享几个真实遇到的案例。

案例一:某渠道包升级一直失败,报INSTALL_FAILED_UPDATE_INCOMPATIBLE。第一反应怀疑签名,但检查打包脚本发现是重签了,没问题。再细看,新旧包居然声明了不同的sharedUserId,旧包属于sharedUserId=A,新包改成了B。系统认为这属于包身份变化,直接拒绝覆盖。解决方式是让渠道方沿用同一个sharedUserId

案例二:应用在低端机上安装后闪退,logcat里报“didn't find class”。先怀疑混淆和分包,但最后查dumpsys package dexopt发现这个包压根没有生成odex,安装时DEX优化被跳过了。清除ART缓存重启设备后恢复正常。这类问题常见于OTA升级后遗留的缓存损坏。

案例三:预装应用第一次开机后不显示。第一反应是扫描失败,但看pm list packages里包是存在的,再查dumpsys package才发现enabled=3(DISABLED_UNTIL_USED)。这是预装配置里带了android:enabledpm disable-until-used的典型结果,不是安装问题。

5.4 logcat快速定位技巧

PMS相关的日志标签主要有PackageManagerPackageManagerServicePackageInstallerDexoptWrapperinstalld。定位安装问题,我建议第一次就开启这几个标签的过滤:

adb logcat -s PackageManager PackageManagerService PackageInstaller

观察日志里是否出现Package [com.xxx] (123) added或者installPackage等关键字。如果日志停在某个校验阶段迟迟不动,再用adb shell dumpsys package installer看会话状态,判断是不是卡在文件暂时区或者等待某个广播。

给个独家小技巧:遇到“安装过程卡死”的问题,不要急着杀pm进程,先抓一份dumpsys package installer和主线程堆栈,看看PackageHandler队列里排了多少任务。我见过不少“卡死”其实是排队任务太多,并不是真的死锁。

6. 我踩过的一些坑与体会

6.1 别在主线程碰PMS

普通应用开发里,PackageManager的接口虽然有个BinderStub的包装,但很多调用走到PMS之后都会做磁盘扫描或全表遍历,耗时不可控。getInstalledPackages()queryIntentActivities()这种接口在包特别多的设备上,几百毫秒很正常。在主线程直接调,流畅度直接被毁。

我自己写系统工具时,凡是涉及全量查询的,一律放到子线程,并且做好结果缓存。PMS内部有大量锁,一个慢查询还可能导致其他调用排队,牵一发动全身。

6.2 安装慢、卡、反复失败未必是APK问题

常见的“安装失败”背后,可能是/data分区 inode 满了、包管理数据库损坏、SELinux标签不正确、甚至低端机器上 dex2oat 排队过长。遇到安装问题,我现在的检查顺序是:先看错误码,再看dumpsys,然后查logcat,最后才怀疑APK本身。顺序反过来,很容易在应用层折腾半天才发现是系统状态异常。

6.3 写系统代码时的一些习惯

做PMS相关定制时会踩到锁的坑。PMS内部有很多锁,像是包信息锁和设置锁,调用顺序如果不一致,两个线程互相等待就可能死锁。我的习惯是:任何自己写的代码里,持有PMS相关锁后绝不再做Binder调用。Binder调用是异步的,可能跨进程、可能阻塞,在锁里做,等于把整个系统大门锁死等着电话铃响。

另外,改包状态类的操作,尽量走系统提供的“事务式”入口,比如PackageInstaller会话,而不是直接改packages.xml。绕过PMS的约束写文件,短期看着能用,下次系统重启或做一次清理就原形毕露。

结尾

做Android系统开发这些年,PMS算是我反复“踩进去”又“爬出来”最深的一个模块。它不像业务代码那样有明确的产品逻辑,更像一张巨大的状态机,把APK的每一点状态变化都记录得清清楚楚。我个人的体会是:别想着一下子读懂所有源码,先把它“管什么、怎么启动、怎么安装、怎么记录状态”这条主线串起来,再带着具体问题去查,会顺畅很多。

最后再分享一个不知道算不算冷门的技巧:当你怀疑应用“装了但没完全装好”时,直接对比pm path 包名返回的路径和/data/app下真实的目录是否存在。命令返回正常但目录消失,基本就是文件系统状态跟PMS内存表不一致,这种问题靠应用日志查不出来,只能在系统层定位。PMS就是一栋大楼的档案室,很多诡异现象,从它这里找答案往往最快。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询