浙江省工程建设协会网站,python做网站框架,南昌网站建设平台,网上做的好金融网站戳蓝字“牛晓伟”关注我哦#xff01;
用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章#xff0c;技术文章也可以有温度。
前言
阅读该篇之前#xff0c;建议先阅读下面的系列文章#xff1a;
Android深入理解包管理–PackageManagerService和它的“小伙伴…戳蓝字“牛晓伟”关注我哦
用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章技术文章也可以有温度。
前言
阅读该篇之前建议先阅读下面的系列文章
Android深入理解包管理–PackageManagerService和它的“小伙伴”
Android深入理解包管理–记录存储模块
Android深入理解包管理–共享库模块
Android深入理解包管理—apk信息
本文摘要
这是包管理系列的最后一篇文章本文的标题是从上帝视角来看PackageManagerService为啥要起这么“狂妄”的名字呢其主要的原因是我希望从一个更全面、更高的、更清晰的视角来看明白PackageManagerService的每个模块之间是如何协作来保证PackageManagerService的关键工作顺利完成。通过本文您将了解到PackageManagerService被划分为哪些模块模块之间是如何协作来保证各项工作的顺利完成。(文中代码基于Android13)
本文大纲
1. 模块的划分
2. 模块的启动
3. 模块相互协作守护apk的安装
4. 模块相互协作守护app的运行
5. 总结
1. 模块划分
其实在Android深入理解包管理–PackageManagerService和它的“小伙伴” 这篇文章已经介绍过PackageManagerService的各个模块了但是我还是希望把它们重新“请”出来以保证后面的内容能顺利连接起来。(当然增加了快照管理模块)
先简单介绍下PackageManagerService它是运行于systemserver进程systemserver进程中有很多很多的服务比如大家熟知的ActivityManagerService、WindowManagerService。而PackageManagerService也是一个服务一个非常非常重要的服务。
下图展示了PackageManagerService的几个关键模块
主要模块有apk管理模块、权限管理模块、共享库模块、记录存储模块、所有apk信息模块、四大组件模块。PackageManagerService不可能只有上面的几个模块它还有快照模块、对外接口模块、property模块等只不过上面的模块较常见。
1.1 权限管理模块
既然是权限管理模块那有必要先来介绍下权限权限分为声明权限和请求权限。
1.1.1 声明权限
声明权限需要在AndroidManifest.xml文件中使用permission标签如下例子
permission android:descriptionstring resourceandroid:icondrawable resourceandroid:labelstring resourceandroid:namestringandroid:permissionGroupstringandroid:protectionLevel[normal | dangerous |signature | ...] /每个apk都可以声明自己的权限那当别的apk访问自己的一些关键信息时候就可以要求它具有某个声明的权限后才可以访问。
1.1.2 请求权限
请求权限就是在AndroidManifest中通过uses-permission标签来使用权限如下代码
uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE/1.1.3 权限管理模块所做的事情
而权限管理模块所做的事情如下
把所有的apk声明的权限收集并集中管理起来保存每个apk请求的权限和请求权限对应的状态请求权限对应的状态是指比如某个apk的请求的权限是否被允许、是否拒绝、是否只是允许一次处理apk权限请求当用户不管是点击了允许、拒绝等都需要经过权限管理模块
权限管理模块把上面这些事情全权交给了PermissionManagerService服务来处理关于权限管理后面会有系列文章来介绍。
1.2 共享库模块
同样先来简单介绍下共享库共享库首先它是一个库库大家肯定非常熟悉了库可以是一个jar文件 (jar包代表java库) 也可以是一个so文件 (so是二进制可执行文件代表native库)而它的形容词共享代表该库是可以被多个程序使用的。共享库也就是可以被多个程序使用的库。在Android中共享库除了上面的意思之外还要再加一条就是共享库是由系统提供的可以被多个程序使用的系统库在Android中共享库可以是一个jar、so、apk文件。
共享库也同样分为声明共享库和使用共享库只有系统apk才能声明共享库。
共享库模块所做的事情如下
把所有声明的共享库收集起来若共享库之间存在依赖则把它们的依赖也初始化若apk中使用了某个共享库则会根据共享库名称、版本信息从共享库模块把共享库的信息查询出来 (如共享库文件路径) 交给该apk
共享库模块把这些事情交给了SharedLibrariesImpl类来处理关于共享库模块的详细介绍可以看Android深入理解包管理–共享库模块 这篇文章。
1.3 记录存储模块
记录存储模块的主要工作是记录apk的安装及附加信息并且把这些信息存储到文件中。也就是要想知道Android设备上有没有安装某个apk是可以从记录存储模块得知的。如果apk安装了则记录存储模块会把安装信息记录下来。
关于该模块的详细介绍可以看 Android深入理解包管理–记录存储模块 这篇文章
1.4 所有apk信息模块
apk信息指的是apk的AndroidManifest.xml文件中配置的各种信息比如apk版本信息、apk包名、声明了哪些四大组件、使用了哪些权限、声明了哪些权限、使用了哪些共享库等。
安装在Android设备上的apk (系统apk和非系统apk)都需要把它们的apk信息存储到内存中以供使用者来查询 (比如某使用者想要根据包名知道某个apk的ApplicationInfo信息)而存放所有apk信息的地方被称为所有apk信息模块。关于apk信息的详细介绍可以看 Android深入理解包管理—apk信息 这篇文章。
下面是所有apk信息模块在PackageManagerService中属性的声明
//下面属性位于PackageManagerService类//key是包名而AndroidPackage存储了解析出的AndroidManifest的信息
final WatchedArrayMapString, AndroidPackage mPackages new WatchedArrayMap();1.5 四大组件模块
四大组件模块在内存中存储了所有已安装apk的AndroidManifest中声明的四大组件四大组件模块的事情是完全交给了ComponentResolver类。
常用于以下场景
ActivityTaskManagerService启动某个Activity时需要从我这获取对应的Activity信息获取到则返回否则启动Activity失效ActivityManagerService启动某个Service时也需要从我这获取对应的Service信息。同理启动某个BroadcastReceiver、ContentProvider也需要从我哦这获取对应的信息
下面是四大组件模块在PackageManagerService中属性的声明
//下面属性位于PackageManagerService类final ComponentResolver mComponentResolver;1.6 apk管理模块
apk管理模块从上图可以看出它在所有模块中的地位是多么重要它包含的功能有扫描所有apk、apk安装/更新/卸载、解析apk。 在后面会详细介绍到它。对apk安装感兴趣可以查看apk安装之谜这篇文章。
1.7 快照管理模块
快照 (snapshot)可以理解为是数据的拷贝在PackageManagerService的各个模块以及各个模块的属性中都充斥着快照如下部分代码
//Settings类//获取Settings的快照
public Settings snapshot() {return mSnapshot.snapshot();
}private Settings(Settings r) {//Settings的很多属性也都有自己的快照mPackages r.mPackagesSnapshot.snapshot();mPackagesSnapshot new SnapshotCache.Sealed();mKernelMapping r.mKernelMappingSnapshot.snapshot();mKernelMappingSnapshot new SnapshotCache.Sealed();省略其他代码······// Do not register any Watchables and do not create a snapshot cache.mSnapshot new SnapshotCache.Sealed();
}上面代码只是截取了Settings (记录存储模块)和它的属性相关的快照其他的各个模块和属性也都有相应的快照。我刚开始看PackageManagerService代码的时候就被各种各样的快照震惊了为啥要有快照呢它的作用是啥呢
快照的作用就是为了快速的检索数据大家都知道PackageManagerService中有各种各样的数据并且数据量都很大为了保持数据的一致性在更新/添加/删除/访问这些数据的时候是都加了各种各样的锁的而如果没有快照的话比如在访问这些数据的时候是需要获取相应的锁如果没有获取到则需要等待想想这个过程是非常影响查询速度的。
那为了解决以上问题快照就诞生了每个模块及相关属性都有自己快照也就是拷贝那当访问这些数据的时候就从快照中直接获取这速度是不是提升了很多 (其实就是以空间换时间罢了)。那当模块或者相关属性的数据发生变化了会做何种处理呢答案是发生变化的模块或属性重新生成自己的快照。
而快照管理模块很显然就是管理了所有的快照而它对应的是Computer类该类是一个接口它的实现类是ComputerEngine快照管理模块还有另外一个作用就是各种数据的代理者PackageManagerService它是一个binder server它的使用者可以通过binder通信的方式从它获取各种数据而获取的各种数据都是先要经过快照管理模块而快照管理模块把各种快照数据组装起来返回给使用方。
PackageManagerService的快照Computer是最顶级的快照它包含了各个模块的快照,而每个模块的快照又包含了自己相关属性的快照。那当某个模块或者模块属性发生变化的时候该变化信息会传递到PackageManagerServicePackageManagerService开始重新收集所有的快照收集过程只要相应的快照没有发生变化则依然使用它否则重新生成快照。
关于快照管理模块先暂时介绍到这。
1.8 小结
那简单总结下各个模块
权限管理模块负责apk权限相关的事情比如请求某个权限apk权限状态存储收集所有apk声明的权限共享库模块负责apk使用到的所有共享库记录存储模块会把apk相关的很多信息记录并且存储到文件中比如apk安装后关于apk安装的信息会存储下来这样就可以供其他使用者检索所有apk信息模块会收集所有已安装apk的AndroidManifest解析出来的信息以供其他使用者检索四大组件模块为了加快检索四大组件的速度会把所有已安装apk的四大组件信息收集起来apk管理模块主要负责apk的安装/卸载/更新它是根基模块因为它的某个功能会对其他模块产生影响。快照管理模块主要目的为加快访问PackageManagerService中的各种数据。
2. 模块的启动
在Android深入理解包管理–PackageManagerService和它的“小伙伴” 中介绍过模块的启动但是我觉得介绍的有些“潦草”故在此更详细的介绍下。
模块的启动主要是想展示给大家在PackageManagerService的启动过程中各个模块的启动都做了啥为啥要这样做
下图展示了PackageManagerService在启动过程中每个模块所做的事情 前几个模块的启动其实都在为扫描所有apk做准备而扫描所有apk是PackageManagerService启动过程中做的非常重要的一件事情如果不扫描所有apk那PackageManagerService就完全不知道当前Android设备上所有已安装apk的具体apk信息 (apk信息指的是apk的AndroidManifest.xml文件中配置的各种信息如apk版本信息、apk包名、声明了哪些四大组件等)不知道具体apk信息PackageManagerService犹如一个废掉的服务不能提供任何有用的服务。
2.1 共享库模块启动
共享库分为内置共享库和声明的共享库。
内置共享库很容易理解就是系统内置的共享库内置共享库可以理解为静态的因为它们的信息被读取到内存后是不会发生变化的。
而声明的共享库指由系统apk声明的共享库。而声明的共享库可以理解为是动态的apk声明了共享库后声明的共享库有可能被删除 (该apk被删除了后者apk删除了该共享库)也有可能被升级 (apk声明的共享库升级了)等。
正因为内置共享库和声明的共享库分别是静态的和动态的也就导致内置共享库信息是可以提前从文件中读取到内存中而声明的共享库它是动态的它的任何变化都会影响到它的使用者因此声明的共享库是不会存储在文件中的需要在扫描所有apk阶段重新收集。
共享库模块的启动主要的工作是初始化内置共享库共享库模块作为PackageManagerService启动的第一个模块提前启动的主要原因是为扫描所有apk做准备的在扫描所有apk的时候有可能某个apk使用了某个内置共享库那这时候肯定需要从共享库模块查出该共享库的信息因此共享库模块的启动要放在前面。
关于共享库模块可以查看共享库模块这篇文章
下面是相关代码自行取阅
//下面代码位于PackageManagerService构造方法中//从 SystemConfig.getInstance() 中把所有的内置共享库信息读取出来ArrayMapString, SystemConfig.SharedLibraryEntry libConfig systemConfig.getSharedLibraries();final int builtInLibCount libConfig.size();for (int i 0; i builtInLibCount; i) {//依次把共享库信息添加到 mSharedLibraries 中mSharedLibraries.addBuiltInSharedLibraryLPw(libConfig.valueAt(i)); }long undefinedVersion SharedLibraryInfo.VERSION_UNDEFINED;//下面代码处理共享库之间的依赖for (int i 0; i builtInLibCount; i) {String name libConfig.keyAt(i);SystemConfig.SharedLibraryEntry entry libConfig.valueAt(i);final int dependencyCount entry.dependencies.length;for (int j 0; j dependencyCount; j) {final SharedLibraryInfo dependency computer.getSharedLibraryInfo(entry.dependencies[j], undefinedVersion);if (dependency ! null) {computer.getSharedLibraryInfo(name, undefinedVersion).addDependency(dependency);}}}2.2 记录存储模块启动
记录存储模块它的主要作用是把记录已安装apk、记录所有apk声明的权限等其中记录已安装apk的信息包括apk的版本号、apk的包名、apk文件路径、apk的appid、apk签名信息、apk请求权限及权限状态等。也就是可以从记录存储模块知道哪个apk安装了apk安装信息都有哪些等。
记录存储模块启动主要的工作就是从已经存储的文件中把存储的信息读取出来交给对应的数据类 (PackageSetting)这样就可以知道安装了哪些apk了。
记录存储模块提前启动的主要原因除了为扫描所有apk做准备还为权限管理模式初始化做准备在扫描所有apk的时候肯定是需要知道某个系统apk是否已经安装已经安装的话就需要用新apk与老apk进行一些比较 (比如签名信息是否一致)。为啥只是针对某个系统apk因为普通apk的安装只能通过正常的用户手动触发而系统apk的安装是在扫描阶段进行的。
关于记录存储模块可以看 记录存储模块 这篇文章
下面是相关代码自行取阅
//下面代码位于PackageManagerService构造方法中//从 /data/system/packages.xml及其他文件中把保存的数据读出来
mFirstBoot !mSettings.readLPw(computer, mInjector.getUserManagerInternal().getUsers(/* excludePartial */ true,/* excludeDying */ false,/* excludePreCreated */ false));2.3 权限管理模块初始化
权限分为声明权限和使用权限不管是apk声明的权限还是apk使用的权限都是在记录存储模块中存储着的当记录存储模块启动的时候这些信息都会被读取出来交给对应的数据类。
权限管理模块初始化就是使用记录存储模块读取到的权限信息来初始自己初始化完毕后就可以从权限管理模块知道所有apk都声明了哪些权限并且可以知道哪些apk使用了哪些权限权限状态是啥 (拒绝、允许等)。
权限管理模块初始化的主要原因同样也是为了扫描所有apk做准备关于权限管理模块后面有相关系列文章介绍。
下面是相关代码自行取阅
//下面代码位于PackageManagerService构造方法中//把所有apk声明的权限交给mPermissionManager
mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
//下面方法会使用每个apk的权限状态初始化自己
mPermissionManager.readLegacyPermissionStateTEMP();2.4 扫描所有apk
扫描所有apk是归apk管理模块上面一直都在提扫描所有apk它是PackageManagerService启动过程中最重要的事情没有之一。为啥要叫扫描apk呢
其实扫描apk是属于安装apk中的重要环节扫描apk对于系统apk的话是一个安装或者升级的过程对于普通apk来说就是一个“查漏补缺”的过程 (比如某个系统apk把声明的某个共享库删除了则使用了该共享库的apk就需要做调整)。扫描apk所做的主要事情如下
把apk信息从AndroidManifest文件中解析出来在依据解析出来的apk信息去记录存储模块查询该apk是否已经安装若安装的话进行升级方面的操作 (比如新老apk进行版本比较);若没有安装则进行安装方面的操作比如为apk创建data目录等。如果apk中AndroidManifest文件中的权限发生了则会通知权限管理模块进行增加/删除/更新对应权限如果apk中声明了共享库 (只有系统apk才可以声明共享库)则会通知共享库模块增加相应的共享库等操作。解析出来的apk信息经过步步验证后最终会存放在所有apk信息模块和四大组件模块这样就可以供其他使用者使用了。
扫描apk最终的结果就是apk信息经过重重校验apk信息最终存放在所有apk信息模块和四大组件模块这样使用者就可以使用这些信息了比如startActivity的时候ActivityTaskManagerService会从四大组件模块中拿到Activity的信息。同时扫描apk也会或多或少的影响到权限管理模块、共享库模块、记录存储模块。(到达如何影响的会在下面apk安装环节介绍)
扫描所有apk分为扫描所有系统apk和扫描所有普通apk先进行扫描所有系统apk后进行扫描所有普通apk为啥是这样的顺序呢原因是这样的只有系统apk才能声明共享库因此需要先把所有的系统apk都扫描完后共享库模块才能把所有声明的共享库收集起来供普通apk来使用。
存放系统apk的目录主要是位于product、vendor、system、system_ext、apex分区下的overlay、app、priv-app、framework目录 (若在分区下存在相应的目录)而位于priv-app目录下的apk是拥有特权的系统apk这里的特权是具有特殊权限的简称。存放普通apk的目录是data分区的app目录下。
扫描所有apk先扫描存放系统apk的目录再扫描存放普通apk的目录如下相关代码
//下面代码位于PackageManagerService构造方法中final int[] userIds mUserManager.getUserIds();
//packageParser的作用是解析apk
PackageParser2 packageParser mInjector.getScanningCachingPackageParser();
//扫描系统apk
mOverlayConfig mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,startTime);
//扫描非系统apk
mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
packageParser.close();2.5 共享库模块再度登场
扫描所有apk是先扫描所有系统apk然后在扫描所有普通apk。而在扫描所有系统apk的时候是没有把系统apk中使用共享库信息补全的 (如果该系统apk确实使用了某个共享库)。
先来解释下啥叫共享库信息补全比如某系统apk在AndroidManifest.xml文件中使用uses-library标签使用了某个共享库共享库信息补全就是从共享库模块把该apk使用的共享库信息 (共享库文件路径、名称等)查询出来并且交给该apk。
那为啥没有把系统apk中使用共享库信息补全呢其主要原因还是因为只有系统apk才能声明共享库比如正在扫描某个系统apk而它使用的共享库对应的系统apk还没有被扫描则这时候该系统apk的共享库就没有补全。那针对这种情况该咋办呢办法就是在扫描完所有apk后在去把所有系统apk中使用的共享库信息一起补全 (如果该系统apk确实使用了某个共享库)。
下面是相关代码请自行取阅
//下面代码位于PackageManagerService构造方法中//下面方法会把所有apk的共享库信息补全 ((如果该apk确实使用了某个共享库)
mSharedLibraries.updateAllSharedLibrariesLPw(null, null, Collections.unmodifiableMap(mPackages)); 2.6 小结
共享库模块、记录存储模块、权限管理模块三个模块的启动为扫描所有apk做准备而扫描所有apk又分为扫描所有系统apk和扫描所有普通apk扫描所有apk最终的结果是所有apk信息最终存放在了所有apk信息模块和四大组件模块这样使用者就可以从PackageManagerService获取到某个apk的信息了比如获取某个Activity的信息。
而扫描所有apk也会或多或少的影响共享库模块、记录存储模块、权限管理模块比如扫描某个系统apk该系统apk声明了新的权限、声明了新的共享库则会把声明的共享库信息记录在共享库模块而声明的权限则会通知权限管理模块增加此权限同时记录存储模块也会把该权限存储到文件中。
3. 模块相互协作守护apk的安装
本节主要想介绍模块之间的相互协作来守护apk的安装先假设要安装的apk在它的AndroidManifest.xml文件中声明了权限、使用了某个权限、使用了某个共享库、声明了四大组件
下图展示了安装该apk的过程
那结合上图来进行介绍。
3.1 apk的安装
apk的安装首先需要把要安装的apk拷贝到/data/app目录下首先先从AndroidManifest.xml中把apk的基础信息 (apk包名、版本号、签名信息等)解析出来其次使用解析出来的基础信息进行apk完整性验证主要验证apk有没有被改过当然还有其他的步骤如果已经安装了该apk则需要验证新老apk的版本信息还需要验证新老apk的签名信息是不是一致这些步骤都验证通过后会从AndroidManifest.xml中把apk的所有信息 (四大组件、权限等等) 都解析出来最后会使用apk信息经过准备、扫描、调和、提交阶段来保证apk最后的安装完成。
上面只是简单的介绍了apk的安装过程关于apk安装更详细的介绍可以看apk安装之谜这篇文章。
3.2 各模块协作
在apk安装的提交阶段会做以下事情
该apk的安装信息 (apk包名、版本信息、apk文件路径、appid、声明和使用的权限等) 以PackageSetting对象添加到记录存储模块记录存储模块会把所有的PackageSetting对象重新写入到文件中该apk信息 (apk包名、声明的四大组件、版本信息、解析的权限等) 添加到所有apk信息模块该apk信息中的声明的四大组件信息添加到四大组件模块因为该apk使用了某个共享库则会从共享库模块把该共享库信息查询出来交给PackageSetting对象因为该apk声明和使用了权限则声明和使用的权限会被添加到权限管理模块。
注对PackageSetting陌生可以看下Android深入理解包管理–记录存储模块 这篇文章
3.3 apk安装最后一步
apk安装的最后一步就是创建data目录和dex优化。创建data目录这样app在运行的时候就可以把私有数据存储在该目录下dex优化可以让app运行更快。
3.4 小结
apk的安装过程小结如下
会在记录存储模块存储下该apk的安装信息 (apk包名、appid、apk路径等)、声明的权限 (若声明了权限)、使用的权限及状态 (若使用了权限并且会把这些信息写入文件apk信息会存放在所有apk信息模块apk声明的四大组件会存放在四大组件模块apk的权限 (若声明了权限或者使用了权限)会存放在 权限管理模块
4. 模块相互协作守护app的运行
上面介绍了模块之间的相互协作守护apk的安装那现在咱们来看下模块之间相互协作来保证app的运行。
大家都知道一个app开始运行可以通过启动一个Activity或者启动一个Service或者启动一个ContentProvider或者启动一个BroadcastReceiver。那我就用启动一个Activity来看PackageManagerService的各个模块是如何守护app运行的这个过程用到的模块主要有快照管理模块、所有apk信息模块、四大组件模块、记录存储模块*这四个模块那就从这三个模块讲起。
4.1 快照管理模块
在启动一个Activity的时候ActivityTaskManagerService会通过Intent信息从PackageManagerService服务查询该Intent对应的Activity信息 (该信息会被封装为ActivityInfo对象)而该查询任务PackageManagerService会交给快照管理模块。
下面是相关代码自行取阅
//ComputerEngine类public final NonNull ListResolveInfo queryIntentActivitiesInternal(Intent intent,String resolvedType, PackageManager.ResolveInfoFlagsBits long flags,PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,int filterCallingUid, int userId, boolean resolveForStart,boolean allowDynamicSplits) {省略代码······}
4.2 四大组件模块
快照管理模块是没有真正的查询服务的而真正的查询Activity信息需要从四大组件模块获取四大组件模块会根据Intent中的ComponentName查询到相应的Activity信息。
如下是相关代码
//ComputerEngine类protected ActivityInfo getActivityInfoInternalBody(ComponentName component,PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {//四大组件模块的实现者就是mComponentResolver调用它的getActivity方法查询到Activity信息ParsedActivity a mComponentResolver.getActivity(component);省略代码······}4.3 所有apk信息模块和记录存储模块
通过启动一个Activity来启动一个app不仅仅只需要Activity的信息还需要Application的信息 (该信息存放在ApplicationInfo对象)为啥还需要ApplicationInfo信息呢
主要原因是当app第一次运行的时候它需要知道apk文件路径、共享库文件路径 (若使用共享库)、包名、app的data目录等等。比如知道了apk文件路径、共享库文件路径 便可以把它们加入到自己的ClassLoader这样app运行时就可以找到自己的类了比如知道了app的data目录就可以知道当前app运行时候存放数据的私有目录在哪了。而以上这些信息都存放在ApplicationInfo对象是需要提前获取到的以便app从ActivityManagerService获取这些信息的时候可以顺利获取到。
而ApplicationInfo的获取是从所有apk信息模块和记录存储模块拿到的记录存储模块存储了共享库信息 (若存在使用共享库)等。
如下代码
//ComputerEngine类protected ActivityInfo getActivityInfoInternalBody(ComponentName component,PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {//四大组件模块的实现者就是mComponentResolver调用它的getActivity方法查询到Activity信息ParsedActivity a mComponentResolver.getActivity(component);AndroidPackage pkg a null ? null : mPackages.get(a.getPackageName());if (pkg ! null mSettings.isEnabledAndMatch(pkg, a, flags, userId)) {PackageStateInternal ps mSettings.getPackage(component.getPackageName());if (ps null) return null;if (shouldFilterApplication(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {return null;}//调用generateActivityInfo方法return PackageInfoUtils.generateActivityInfo(pkg,a, flags, ps.getUserStateOrDefault(userId), userId, ps);}省略代码······}//PackageInfoUtils类private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,PackageManager.ComponentInfoFlagsBits long flags,NonNull PackageUserStateInternal state, Nullable ApplicationInfo applicationInfo,UserIdInt int userId, Nullable PackageStateInternal pkgSetting) {省略代码······//构建ApplicationInfoif (applicationInfo null) {applicationInfo generateApplicationInfo(pkg, flags, state, userId, pkgSetting);}if (applicationInfo null) {return null;}//构建ActivityInfofinal ActivityInfo info PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(a, flags, applicationInfo);assignSharedFieldsForComponentInfo(info, a, pkgSetting, userId);return info;}4.4 小结
不论是启动一个Activity或者启动一个Service或者启动一个ContentProvider或者启动一个BroadcastReceiver的方式来启动一个app都需要去PackageManagerService查询相应的组件信息和ApplicationInfo信息只有查询到正确的信息后才能执行下一步操作否则停止启动过程。
5. 总结
本文主要是通过“上帝的视角”来看清楚PackageManagerService的各模块是如何保证PackageManagerService的主要工作如何进行的在PackageManagerService启动过程每个模块都做了哪些提前的准备工作来保证扫描所有apk工作的顺利完成在apk安装过程中apk信息会存储在所有apk信息模块和四大组件模块当然在安装过程中或多或少还会用到其他模块在app运行时需要从PackageManagerService的相关模块中获取对应组件和ApplicationInfo信息来保证后面启动流程的正确执行关于包管理系列的文章就全部介绍完毕。