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.mzph.cn/web/55693.shtml

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

相关文章

HTB:Bashed[WriteUP]

目录 连接至HTB服务器并启动靶机 1.How many open TCP ports are listening on Bashed? 2.What is the relative path on the webserver to a folder that contains phpbash.php? 3.What user is the webserver running as on Bashed? 执行命令&#xff1a;whoami 4.S…

GraphRAG 与 RAG 的比较分析,收藏这一篇就够了!!!

检索增强生成&#xff08;RAG&#xff09;技术概述 检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;简称 RAG&#xff09;是一种旨在提升大型语言模型&#xff08;Large Language Models&#xff0c;LLMs&#xff09;性能的技术方法。其核心思想是通过整…

5 -《本地部署开源大模型》在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战

在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战 无论是在单机单卡&#xff08;一台机器上只有一块GPU&#xff09;还是单机多卡&#xff08;一台机器上有多块GPU&#xff09;的硬件配置上启动ChatGLM3-6B模型&#xff0c;其前置环境配置和项目文件是相同的。如果大家对配置过程还…

如何给手机换ip地址

在当今数字化时代&#xff0c;IP地址作为设备在网络中的唯一标识&#xff0c;扮演着举足轻重的角色。然而&#xff0c;有时出于隐私保护、网络访问需求或其他特定原因&#xff0c;我们可能需要更改手机的IP地址。本文将详细介绍几种实用的方法&#xff0c;帮助您轻松实现手机IP…

一元n次多项式乘法【数据结构-链表】

一元n次多项式定义如下&#xff1a; 其中Ai​为实数&#xff0c;i为不小于0的整数。在完成“一元n次多项式输入输出”题目的基础上实现一元n次多项式的乘法。要求使用链表实现上述运算。 输入格式: 有两个一元n次多项式&#xff0c;格式分别为&#xff1a; f(X)3X2 X1 g(X)−…

MySQL 知识点_01

1、DISTINCT select DISTINCT EMPLOYEE_ID ,FIRST_NAME from employees 按照ID去重&#xff0c;DISTINCT的字段要放在前面&#xff0c;不会再继续在FIRST_NAME上去重判断&#xff1b; 如果需要多字段去重&#xff0c;需要用到group by&#xff0c;这个后面讲&#xff1b; …

一次恶意程序分析

首先F12shift查看字符表 字符表发现可疑字符串 双击进入 再tab 进入这里 推测为main函数 可见一些可疑的api FindResourceW推测该木马使用了资源加载 VirtualAlloc申请内存 然后sub_1400796E0 有 dwSize 参数 推测为 拷贝内存 memcpy类似函数 、 然后sub_140078CB0函数 跟进函…

HarmonyOS NEXT 应用开发实战(五、页面的生命周期及使用介绍)

HarmonyOS NEXT是华为推出的最新操作系统&#xff0c;arkUI是其提供的用户界面框架。arkUI的页面生命周期管理对于开发者来说非常重要&#xff0c;因为它涉及到页面的创建、显示、隐藏、销毁等各个阶段。以下是arkUI页面生命周期的介绍及使用举例。 页面的生命周期的作用 页面…

【正点原子K210连载】第四十六章 车牌识别实验 摘自【正点原子】DNK210使用指南-CanMV版指南

第四十六章 车牌识别实验 在上一章节中&#xff0c;介绍了利用maix.KPU模块实现了通过提取图像中人脸的特征进行人脸识别&#xff0c;本章将继续介绍利用maix.KPU模块实现的车牌识别。通过本章的学习&#xff0c;读者将学习到车牌识别应用在CanMV上的实现。 本章分为如下几个小…

视觉识别技术:开启智能视觉新时代

引言 在数字化时代&#xff0c;信息的获取和处理变得前所未有的重要。视觉识别技术&#xff0c;作为人工智能领域的一个重要分支&#xff0c;正在逐渐改变我们与数字世界的互动方式。它通过模拟人类视觉系统&#xff0c;使计算机能够识别和理解图像和视频中的内容&#xff0c;…

Shell案例之一键部署mysql

1.问题 我认为啊学习就是一个思考的过程&#xff0c;思考问题的一个流程应该是&#xff1a;提出问题&#xff0c;分析问题&#xff0c;解决问题 在shell里部署mysql服务时&#xff0c;我出现一些问题&#xff1a; 1.安装mysql-server时&#xff0c;没有密钥&#xff0c;安装…

普通java web项目集成spring-session

之前的老项目&#xff0c;希望使用spring-session管理会话&#xff0c;存储到redis。 项目环境&#xff1a;eclipse、jdk8、jetty嵌入式启动、非spring项目。 实现思路&#xff1a; 1.添加相关依赖jar。 2.配置redis连接。 3.配置启动spring。 4.配置过滤器&#xff0c;拦…

L1练习-鸢尾花数据集处理(分类/聚类)

背景 前文&#xff08;《AI 自学 Lesson1 - Sklearn&#xff08;开源Python机器学习包&#xff09;》&#xff09;以鸢尾花数据集的处理为例&#xff0c;本文将完善其代码&#xff0c;在使用 sklearn 的部分工具包基础上&#xff0c;增加部分数据预处理、数据分析和数据可视化…

QUIC 协议的优势

QUIC 协议的优势包括&#xff1a; 快速建立连接&#xff1a;将传输层和加密层的握手合并&#xff0c;减少了连接建立的延迟。QUIC 建连时间大约为 0~1RTT&#xff0c;相比 HTTPS 的 3RTT 建连&#xff0c;具有极大的优势。客户端第一次建连的握手协商需 1RTT&#xff0c;而已建…

Linux 和Windows创建共享文件夹实现文件共享

直接开整 1.Windows下创建共享文件夹share右击-》属性—》共享-》选择所有人-》点击共享 2.共享创建完成后可以使他的共享网络地址或者Windows ip地址-推荐使用Windows ip地址有时候 不知道什么原因他Linux解析不了网络地址 共享网络地址 —共享文件夹share 右击-》属性—》共…

扫普通链接二维码打开小程序

1. 2.新增规则&#xff08;注意下载文件到跟目录下&#xff0c;需要建个文件夹放下载的校验文件&#xff09; 3.发布 ps&#xff1a;发布后&#xff0c;只能访问正式版本。体验版本如果加了 测试链接http://xxx/xsc/10 那么http://xxx/xsc/aa.....应该都能访问 例如aa101 aa…

CMOS晶体管的串联与并联

CMOS晶体管的串联与并联 前言 对于mos管的串联和并联&#xff0c;一直没有整明白&#xff0c;特别是设计到EDA软件中&#xff0c;关于MOS的M和F参数&#xff0c;就更困惑了&#xff0c;今天看了许多资料以及在EDA软件上验证了电路结构与版图的对应关系&#xff0c;总算有点收…

VScode中CMake无高亮(就是没有补全的提示)

在我学的过程中我发现我的CMake是这样的&#xff0c;如下图 但在教学视频里是这样的&#xff08;如下图&#xff09; 这非常的难受&#xff0c;所以疯狂的找&#xff0c;最后是CMake报错有 原因就是&#xff1a;本地没有配置环境变量&#xff0c;解决方法是下一个cmake然后直接…

STM32-CubeIDE用串口通讯

USART串口通讯 一、轮询模式 1.设置所接引脚为UART异步模式 选择完成CTRLS保存。 2.编写测试代码&#xff08;自动发送hello world&#xff09; 在mian函数里面编写代码 原函数 调用函数&#xff0c;需要数据类型一致&#xff0c;使用函数通过串口发送数组里面的数据 打开串…

延迟队列实现及其原理详解

1.绪论 本文主要讲解常见的几种延迟队列的实现方式&#xff0c;以及其原理。 2.延迟队列的使用场景 延迟队列主要用于解决每个被调度的任务开始执行的时间不一致的场景&#xff0c;主要包含如下场景: 1.比如订单超过15分钟后&#xff0c;关闭未关闭的订单。 2.比如用户可以…