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: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.rhkb.cn/news/447324.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

苹果正式宣布:iPhone全面开放近场通信(Near Field Communication,简称NFC)【使用安全元件提供app内NFC数据交换功能】

文章目录 引言I iPhone的NFC功能开发者用户数据交换的体验革新安全与隐私II 知识扩展:近场通信(NFC)技术钱包NFC开关打开读取NFC标签(NFC tags )权限demo引言 2014年iPhone 6开始,苹果首次引入了NFC功能,但最初只允许自家的Apple Pay进行移动支付。慢慢地适配了交通卡,增…

【CSS】纯css3螺旋状loading加载特效

效果图 <div class"ai-loader"><div class"dot"></div><div class"dot"></div><div class"dot"></div><div class"dot"></div><div class"dot">&…

【AI知识点】三种不同架构的大语言模型(LLMs)的区别

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】 在自然语言处理&#xff08;NLP&#xff09;中&#xff0c;预训练语言模型&#xff08;LLMs, Large Language Models&#xff09;通常基于不同的架构&#xff0c;如仅编码器的模型&#xff08;Encoder-only&#xff09;、…

量子计算机的原理与物理实现

量子计算机的原理与物理实现很复杂 指导性原则 首先思考制备一台量子计算机需要些什么&#xff1f; 需要量子比特——二能级量子系统。除了量子计算机需要满足一些物理特性&#xff0c;它还必须要把量子比特绘制到某种初态上&#xff0c;以及测量系统的输出态。 而实验上的挑战…

基于SSM+微信小程序的实验室设备故障报修管理系统2

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的实验室设备故障报修管理系统2实现了管理员&#xff0c;用户&#xff0c;维修员三个角色。 管理员功能有 个人中心&#xff0c;用户管理&#xff0c;维修员管理&#…

[网络基础]——ICMP(互联网控制消息协议)协议介绍

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;网络通信基础TCP/IP专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年10月15日8点23分 在现代网络通信中&#xff0c;互联网控制消息协议&#xff08;ICMP&#xff09;扮演着至关重要的角色…

使用Go语言的gorm框架查询数据库并分页导出到Excel实例

文章目录 基本配置配置文件管理命令行工具: Cobra快速入门基本用法 生成mock数据SQL准备gorm自动生成结构体代码生成mock数据 查询数据导出Excel使用 excelize实现思路完整代码参考 入口文件效果演示分页导出多个Excel文件合并为一个完整的Excel文件 完整代码 基本配置 配置文…

Java项目实战II基于Spring Boot的问卷调查系统的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 基于Spring…

第十三章 RabbitMQ之消息幂等性

目录 一、引言 二、消息幂等解决方案 2.1. 方案一 2.2. 方案二 一、引言 幂等是一个数学概念&#xff0c;用函数表达式来描述是这样的&#xff1a;f(x) f(f(x)) 。在程序开发中&#xff0c;则是指同一个业务&#xff0c;执行一次或多次对业务状态的影响是一致的。有些业务…

【Web——HTML 初阶】网页设计标题

♥HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是构建网页和Web应用的基础语言之一。它不是一种编程语言&#xff0c;而是一种标记语言&#xff0c;用于描述网页的结构和内容。HTML使用标签&#xff08;tags&#xff09;来标记不同类型的…

Jetpack-Room

Room是Android Jetpack中的一个组件&#xff0c;它提供了一个抽象层&#xff0c;帮助开发者在本地数据库&#xff08;如SQLite&#xff09;上进行持久化数据存储。Room通过简化数据库操作&#xff0c;使得数据管理变得更加容易和高效。 Room重要的概念 实体&#xff08;Entit…

[实时计算flink]CREATE DATABASE AS(CDAS)语句

CDAS支持整库级别的表结构和数据的实时同步&#xff0c;还支持表结构变更的同步。本文为您介绍CREATE DATABASE AS&#xff08;CDAS&#xff09;的使用方法&#xff0c;并提供了多种使用场景下的示例。 背景信息 CDAS是CTAS语法的一个语法糖&#xff0c;用于实现整库同步、多…

CesiumLab介绍

软考鸭小程序 学软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 CesiumLab是一个围绕Cesium平台设计的完整易用的数据预处理工具集&#xff0c;它旨在最大化提升三维数据可视化效率。本文将详细介绍CesiumLab的安装、主要功能…

【stm32】DMA的介绍与使用

DMA的介绍与使用 1、DMA简介2、存储器映像3、DMA框图4、DMA基本结构5、DMA请求6、数据宽度与对齐7、数据转运DMA&#xff08;存储器到存储器的数据转运&#xff09;程序编写&#xff1a; 8、ADC连续扫描模式DMA循环转运DMA配置&#xff1a;程序编写&#xff1a; 1、DMA简介 DM…

C++笔记之类三种的继承方式

C++笔记之类三种的继承方式 code review! 文章目录 C++笔记之类三种的继承方式1.《C++ Primer Plus》(第6版)中文版Page 5502.C++类继承方式与能否隐式向上转换的关系1.《C++ Primer Plus》(第6版)中文版Page 550 除基类私有成员变量外(基类公有成员变量和保护成员变量):…

Element-ui官方示例(Popover 弹出框)

Element-ui官方示例&#xff08;Popover 弹出框&#xff09;&#xff0c;好用的弹出框。 使用 vue-cli3 我们为新版的 vue-cli 准备了相应的​Element 插件​&#xff0c;你可以用它们快速地搭建一个基于 Element 的项目。 使用 Starter Kit 我们提供了通用的项目模版&#…

gitLab配置ssh

1打开git命令行&#xff0c;创建秘钥 ssh-keygen -t rsa -b 4096 -C "用户名xxx.com" 2执行下面的命令查看公钥 cat ~/.ssh/id_rsa.pub 3#复制公钥到gitlab网址上ssh页面添加ssh的key&#xff08;公钥&#xff09; 4本地的git命令行中添加账户邮箱 git config -…

Windows7 X64 成功安装 .NET Framework 4.8 的两种方法

Windows7 X64 成功安装 .NET Framework 4.8 的两种方法 windows7系统SP1安装完成后&#xff0c;在安装某软件时&#xff0c;提示需要先安装4.6以上的版本net-framework包&#xff0c;正好电脑里有个net-framework4.8软件包&#xff0c;于是打算用上&#xff0c;可是在安装时&a…

JDK17下,使用SHA1算法报Certificates do not conform to algorithm constraints错误

JDK17从17.0.5开始&#xff0c;默认不再允许使用SHA1算法&#xff0c;如果引用的jar包或代码里使用了SHA1算法&#xff0c;会报以下错误。 Caused by: javax.net.ssl.SSLHandshakeException: Certificates do not conform to algorithm constraintsat java.base/sun.security.…

JS开发es8266板子,搞着玩-MAX7219模块 远程显示led字符串

JS开发es8266板子&#xff0c;搞着玩-MAX7219模块 板子为 esp8266 这里接了两个8x8 Led.Matrix espjs https://www.espruino.com/ 我是看了,这个文章 发现js可以开发esp板子的就尝试了下远程点灯&#xff0c;挺有意思就买了很多模块慢慢尝试 代码 这里我把wifi模块又包了一…