Android从上帝视角来看PackageManagerService

devtools/2024/10/18 11:05:53/

戳蓝字“牛晓伟”关注我哦!

用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章,技术文章也可以有温度。

前言

阅读该篇之前,建议先阅读下面的系列文章:

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:description="string resource"android:icon="drawable resource"android:label="string resource"android:name="string"android:permissionGroup="string"android:protectionLevel=["normal" | "dangerous" |"signature" | ...] />

每个apk都可以声明自己的权限,那当别的apk访问自己的一些关键信息时候就可以要求它具有某个声明的权限后才可以访问。

1.1.2 请求权限

请求权限就是在AndroidManifest中通过uses-permission标签来使用权限,如下代码:

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

1.1.3 权限管理模块所做的事情

权限管理模块所做的事情如下:

  1. 把所有的apk声明的权限收集并集中管理起来
  2. 保存每个apk请求的权限和请求权限对应的状态,请求权限对应的状态是指比如某个apk的请求的权限是否被允许是否拒绝是否只是允许一次
  3. 处理apk权限请求,当用户不管是点击了允许拒绝等,都需要经过权限管理模块

权限管理模块把上面这些事情全权交给了PermissionManagerService服务来处理,关于权限管理后面会有系列文章来介绍

1.2 共享库模块

同样先来简单介绍下共享库,共享库首先它是一个,库大家肯定非常熟悉了,库可以是一个jar文件 (jar包代表java库) 也可以是一个so文件 (so是二进制可执行文件代表native库),而它的形容词共享代表该库是可以被多个程序使用的。共享库也就是可以被多个程序使用的库。在Android中共享库除了上面的意思之外,还要再加一条就是共享库是由系统提供的可以被多个程序使用的系统库,在Android中共享库可以是一个jar、so、apk文件。

共享库也同样分为声明共享库使用共享库,只有系统apk才能声明共享库。

共享库模块所做的事情如下:

  1. 把所有声明的共享库收集起来,若共享库之间存在依赖,则把它们的依赖也初始化
  2. 若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 WatchedArrayMap<String, AndroidPackage> mPackages = new WatchedArrayMap<>();

1.5 四大组件模块

四大组件模块在内存中存储了所有已安装apk的AndroidManifest中声明的四大组件,四大组件模块的事情是完全交给了ComponentResolver类。

常用于以下场景:

  1. ActivityTaskManagerService启动某个Activity时,需要从我这获取对应的Activity信息,获取到则返回;否则启动Activity失效
  2. ActivityManagerService启动某个Service时,也需要从我这获取对应的Service信息。同理启动某个BroadcastReceiverContentProvider也需要从我哦这获取对应的信息

下面是四大组件模块在PackageManagerService中属性的声明:

//下面属性位于PackageManagerService类final ComponentResolver mComponentResolver;

1.6 apk管理模块

apk管理模块从上图可以看出它在所有模块中的地位是多么重要,它包含的功能有扫描所有apkapk安装/更新/卸载解析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 小结

那简单总结下各个模块:

  1. 权限管理模块负责apk权限相关的事情,比如请求某个权限,apk权限状态存储,收集所有apk声明的权限
  2. 共享库模块负责apk使用到的所有共享库
  3. 记录存储模块会把apk相关的很多信息记录并且存储到文件中,比如apk安装后关于apk安装的信息会存储下来,这样就可以供其他使用者检索
  4. 所有apk信息模块会收集所有已安装apk的AndroidManifest解析出来的信息,以供其他使用者检索
  5. 四大组件模块为了加快检索四大组件的速度,会把所有已安装apk的四大组件信息收集起来
  6. apk管理模块主要负责apk的安装/卸载/更新,它是根基模块,因为它的某个功能会对其他模块产生影响。
  7. 快照管理模块主要目的为加快访问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() 中把所有的内置共享库信息读取出来ArrayMap<String, 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的appidapk签名信息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所做的主要事情如下:

  1. 把apk信息从AndroidManifest文件中解析出来
  2. 在依据解析出来的apk信息,去记录存储模块查询该apk是否已经安装,若安装的话进行升级方面的操作 (比如新老apk进行版本比较);若没有安装则进行安装方面的操作,比如为apk创建data目录等。
  3. 如果apk中AndroidManifest文件中的权限发生了则会通知权限管理模块进行增加/删除/更新对应权限;如果apk中声明了共享库 (只有系统apk才可以声明共享库),则会通知共享库模块增加相应的共享库等操作。
  4. 解析出来的apk信息经过步步验证后,最终会存放在所有apk信息模块四大组件模块,这样就可以供其他使用者使用了。

扫描apk最终的结果就是apk信息经过重重校验,apk信息最终存放在所有apk信息模块和四大组件模块,这样使用者就可以使用这些信息了,比如startActivity的时候ActivityTaskManagerService会从四大组件模块中拿到Activity的信息。同时扫描apk也会或多或少的影响到权限管理模块共享库模块记录存储模块。(到达如何影响的会在下面apk安装环节介绍)

扫描所有apk分为扫描所有系统apk扫描所有普通apk,先进行扫描所有系统apk,后进行扫描所有普通apk,为啥是这样的顺序呢?原因是这样的只有系统apk才能声明共享库,因此需要先把所有的系统apk都扫描完后,共享库模块才能把所有声明的共享库收集起来,供普通apk来使用。

存放系统apk的目录主要是位于productvendorsystemsystem_extapex分区下的overlayapppriv-appframework目录 (若在分区下存在相应的目录),而位于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的过程
image

那结合上图来进行介绍。

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安装的提交阶段会做以下事情:

  1. 该apk的安装信息 (apk包名、版本信息、apk文件路径、appid、声明和使用的权限等) 以PackageSetting对象添加到记录存储模块,记录存储模块会把所有的PackageSetting对象重新写入到文件中
  2. 该apk信息 (apk包名、声明的四大组件、版本信息、解析的权限等) 添加到所有apk信息模块
  3. 该apk信息中的声明的四大组件信息添加到四大组件模块
  4. 因为该apk使用了某个共享库,则会从共享库模块把该共享库信息查询出来交给PackageSetting对象
  5. 因为该apk声明和使用了权限,则声明和使用的权限会被添加到权限管理模块

注:对PackageSetting陌生,可以看下Android深入理解包管理–记录存储模块

这篇文章

3.3 apk安装最后一步

apk安装的最后一步就是创建data目录dex优化。创建data目录这样app在运行的时候就可以把私有数据存储在该目录下,dex优化可以让app运行更快。

3.4 小结

apk的安装过程,小结如下:

  1. 会在记录存储模块存储下该apk的安装信息 (apk包名、appid、apk路径等)、声明的权限 (若声明了权限)、使用的权限及状态 (若使用了权限),并且会把这些信息写入文件
  2. apk信息会存放在所有apk信息模块
  3. apk声明的四大组件会存放在四大组件模块
  4. 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 List<ResolveInfo> 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信息来保证后面启动流程的正确执行,关于包管理系列的文章就全部介绍完毕。

请添加图片描述


http://www.ppmy.cn/devtools/126705.html

相关文章

《拿下奇怪的前端报错》:1比特丢失导致的音视频播放时长无限增长-浅析http分片传输核心和一个坑点

问题背景 在一个使用MongoDB GridFS实现文件存储和分片读取的项目中&#xff0c;同事遇到了一个令人困惑的问题&#xff1a;音频文件总是丢失最后几秒&#xff0c;视频文件也出现类似情况。更奇怪的是&#xff0c;播放器显示的总时长为无限大。这个问题困扰了团队成员几天&…

【RS】GEE(Python):栅格计算

在遥感影像处理中&#xff0c;栅格计算是一项至关重要的操作。栅格数据代表了地球表面特定范围内的物理量信息&#xff0c;利用栅格计算可以进行多种分析操作&#xff0c;比如计算植被指数、分类、过滤、组合波段&#xff0c;甚至执行复杂的空间分析任务。本篇教程将详细介绍遥…

【学习笔记】MongoDB 概念

文章目录 MongoDB 概念MongoDb 的应用场景什么时候会选择MongoDB&#xff1f; MongoDB 概念 MongoDb 的应用场景 传统的关系型数据库(如MySQL)&#xff0c;在数据操作的三高需求以及应对Web2.0的网站需求面前&#xff0c;显得力不从心。 那什么是“三高”&#xff1f; 高血…

rollup.js 插件实现原理与自定义

Rollup.js 是一个JavaScript模块打包器&#xff0c;它主要用于将小块代码编译成大块复杂的库或应用程序。相较于Webpack&#xff0c;Rollup更专注于代码的ES模块转换和优化&#xff0c;特别适合构建库或者那些对代码体积、执行效率有严格要求的应用。Rollup的核心特性之一就是它…

鸿蒙NEXT开发-知乎评论小案例(基于最新api12稳定版)

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

机器学习和深度学习的差别

定义和基本原理 机器学习&#xff1a; 定义&#xff1a;机器学习是一种让计算机自动从数据中学习规律和模式的方法&#xff0c;无需明确编程。它通过构建数学模型&#xff0c;利用已知数据进行训练&#xff0c;然后对新的数据进行预测或决策。基本原理&#xff1a;机器学习算…

Flink移除器Evictor

前言 在 Flink 窗口计算模型中&#xff0c;数据被 WindowAssigner 划分到对应的窗口后&#xff0c;再经过触发器 Trigger 判断窗口是否要 fire 计算&#xff0c;如果窗口要计算&#xff0c;会把数据丢给移除器 Evictor&#xff0c;Evictor 可以先移除部分元素再交给 ProcessFu…

【Linux】< 条件等待>解决< 线程饥饿问题 >——【多线程同步问题】

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Lin…