PKMS启动详解(五)之Android包信息体和包解析器(中)
Android PackageManagerService系列博客目录:
PKMS启动详解系列博客概要
PKMS启动详解(一)之整体流程分析
PKMS启动详解(二)之怎么通过packages.xml对已安装应用信息进行持久化管理?
PKMS启动详解(三)之BOOT_PROGRESS_PMS_START流程分析
PKMS启动详解(四)之Android包信息体和包解析器(上)
PKMS启动详解(五)之Android包信息体和包解析器(中)
PKMS启动详解(六)之Android包信息体和包解析器(下)
PKMS启动详解(七)之BOOT_PROGRESS_PMS_SYSTEM_SCAN_START阶段流程分析
PKMS启动详解(八)之BOOT_PROGRESS_PMS_DATA_SCAN_START阶段流程分析
引言
在前面的博客PKMS启动详解(四)之Android包信息体和包解析器(上)中我们重点从Android包管理机制的设计者角度出发,着重分析了:
- Android包管理机制中的Android包信息体设计思想和源码逻辑
- Android包管理机制中的Android包解析器PackageParser设计思想
那么在今天的博客中,我们将再接再厉,一鼓作气的继续分析Android包解析器PackageParser源码实现逻辑!
在正式开始本篇的博客相关分析前,还是非常有必要放出包解析器PackageParser的核心结构图(前面是借用其他博主的,这里就自己花费些时间单独画了):
同时本篇博客对于包解析器PackageParser类源码,会遵从如下几个方面来进行入手来分析(也可以人为是本篇博客的中心):
1.从包管理机制设计者意图了解它,即它的概述和定义是什么
2.它作为一个工具类,它的核心成员变量是什么(当然是和包信息体的封装有关)
3.它作为一个工具类,它的核心方法有那些(核心方法就是解析Android包)
注意:本篇的介绍是基于Android 7.xx平台为基础的(并且为了书写简便后续PackageManagerService统一简称为PKMS),其中涉及的代码路径如下:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/core/java/android/content/pm/PackageParser.java
一.从源码的角度概述Android包解析器PackageParser
在前面的博客中,我们从理论上知道了包解析器PackageParser是Android包管理机制设计的一个用于解析Android包的工具类,但是对于它的具体实现我们知之甚少,从这个章节开始我们就从源码角度出发对其展开猛烈的攻势,争取将其一举拿下!实干出真知!
通常了解一个人首先是从看脸开始(也不排除其它的位置啊,哈哈!),而看Android源码类通常可以从设计者着对它的概述开始,我们这里对于包解析器PackageParser的深入了解也不能免俗的遵循这个定理,我们看看设计者是如何对它进行定义的,如下:
// 【 PackageParser.java】
/**
* Parser for package files (APKs) on disk. This supports apps packaged either
* as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
* APKs in a single directory.
* Apps packaged as multiple APKs always consist of a single "base" APK (with a
* null split name) and zero or more "split" APKs (with unique split
* names). Any subset of those split APKs are a valid install, as long as the
* following constraints are met:
* All APKs must have the exact same package name, version code, and signing certificates.
* All APKs must have unique split names.
* All installations must contain a single base APK.
*
* @hide
*/
public class PackageParser {
...
}
对于上述的描述,英文比较好的读者估计连蒙带猜基本能内部消化了,对于不能内部消化的只能且听我来分解了:
上述的注释非常经典没有过多的拖泥带水,我们对其强撸一番!
1.PackageParser是用于磁盘上包文件(APKs)的解析器。它即支持打包成单个“单块”APK的应用程序的解析,同时也支持打包成多个APK在单个目录中的“集群”应用程序的解析
2.一个"集群"APK有一个"基准"APK(base APK)组成和其他一些"分割"APK(“split” APKs)构成,其中这些"分割"APK用一些数字来分割。这些"分割"APK的必须都是有效的安装,同时必须满足下面的几个条件:
- 所有APKs必须具有完全相同的包名、版本代码和签名证书。
- 所有APK必须有唯一的拆分名称。
- 所有安装必须包含一个单一的APK
3.并且该类是一个hide类,说明它只能用于Android内部使用,不能被第三方应用使用
1.1 Android安装包的形态
在前面的注释中,多次提到了single APK和multiple APKS这两个概念,那么它们纠结表示什么呢!我们们分别来说(其实从单复数就说明情况了)。
1.1.1 Single APK
对于Single APK就比较好理解了,它通常所开发的APK,即每一个应用安装包有且只存在一个.apk文件,形如下面的形式:
xxx:/system/system_ext/priv-app/Settings $ ls
Settings.apk
1.1.2 Multiple APKS
在Android L(5.0)以后,支持APK拆分,即一个APK可以分割成很多部分,位于相同的目录下,每一个部分都是一个单独的APK文件,所有的APK文件具备相同的签名,在APK解析过程中,会将拆分的APK重新组合成内存中的一个Package。对于一个完整的APK,Android称其为signle APK;对于拆分后的APK,Android称其为multiple APKS。此时形如下面的形式:
xxx:/system/system_ext/priv-app/Settings $ ls
Settings-base.apk Settings-split.apk
对于有过一定Android开发经验的读者一定知道在Android L(5.0)以前,APK文件都是直接位于app或priv-app目录下,譬如短彩信APK的目录就是/system/priv-app/Mms.apk;到了Android L(5.0)之后,多了一级目录结构,譬如短彩信APK的目录是/system/priv-app/Mms/Mms.apk,这是Android为了支持APK拆分而做的改动,如果要将短彩信APK进行拆分,那所有被拆出来的APK都位于/system/priv-app/Mms/即可,这样在包解析时,就会变成以multiple APKS的方式解析目录。
并且这里有一点需要注意,对于/system/priv-app/Mms/下面的安装包,无论他是否multiple APKS形式的,最后都会调用parseClusterPackage方法来进行解析,这个读者一定要注意,注意!千万不要把Multiple APKS和Cluster解析混淆了!它们之间的前提是Multiple APKS一定是采取Cluster解析方式,但是采取Cluster不一定是Multiple APKS也可能是Single APK。
关于Multiple APKS详细介绍可以参看谷歌官网多APK支持!
1.1.3 不同安装包的解析方式
对于Android安装包的形式我们理解清楚了,从而会导致包解析器PackageParser对于上述二者会采取不同的解析方式,但是最后都会徐途同归(至于怎么具体解析的,后续详细分析)!
并且这里再次强调一点,对于Single APK安装包采取parseMonolithicPackage()还是parseClusterPackage()的解析方式,这个取决于安装包的路径是否是一个文件夹!注意,注意,注意!
1.2 包解析器PackageParser解析Android包的步骤
通过前面的努力我们知道了,PackageParser的核心功能就是解析各种类型的Android包(其实也就两种而已),而解析包的最最主要逻辑就是解析APK文件,在正式开始PackageParser解析Android包之前,这里我们先给出解析一个APK主要是分为两个步骤:
- 将APK解析成Package数据结构:即解析APK文件为Package对象的过程。
此处是我们今天博客的重点!
- 将解析得到的Package填充为PackageInfo数据结构:即由Package对象生成Package对象生成PackageInfo包信息体的过程
关于Android包信息体我们在前面的博客中已经重点的介绍过,重点关注它是怎么被填充的
二.PackageParser重要成员
对于PackageParser我们该了解的不该了解的都了解了,是时候对其展开真刀真枪的实干了。这里我们先从其成员(或者是内部类)入手展开!在PackageParser中定义了非常多的内部类用来解析PackageParser,如下所示:
2.1 IntentInfo内部类
通过前面的博客总结我么可知,对于APK中的androidmanifest.xml的<activity>,<service>标签,都可以配置<intent-filter>,来过滤其可以接收的Intent,这些信息也需要在包解析器中体现出来,为此组件Component依赖于IntentInfo这个数据结构。每一个具体的组件所依赖的IntentInfo不同,所以Component和IntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。
所以这里我们就来看下IntentInfo的实现。
没有啥好说的,直接来看源码!
// 【 PackageParser.java 】
public class PackageParser {
...
/*
这里可以看到IntentInfo就是对IntentFilter意图过滤器的再次封装,
它是承载包组件<intent-filter>标签的数据结构
*/
public static class IntentInfo extends IntentFilter {
public boolean hasDefault;
public int labelRes;
public CharSequence nonLocalizedLabel;
public int icon;
public int logo;
public int banner;
public int preferred;
}
...
}
这里这里可以看到IntentInfo就是对IntentFilter意图过滤器的再次封装,它是用来承载包组件<intent-filter>标签的数据结构。最终我们会得到如下的类图:
最后我们补充一点,对于IntentFilter我想读者肯定很熟悉了,它就是江湖人称的意图过滤器,通常它的过滤规则过滤规则包含以下三个方面:
- Action: 每一个IntentFilter可以定义零个或多个标签,如果Intent想要通过这个IntentFilter,则Intent所辖的Action需要匹配其中至少一个。
- Category: 每一个IntentFilter可以定义零个或多个标签,如果Intent想要通过这个IntentFilter,则Intent所辖的Category必须是IntentFilter所定义的Category的子集,才能通过IntentFilter。譬如Intent设置了两个Category:CATEGORY_LAUNCHER和CATEGORY_MAIN,只有那些至少定义了这两项Category的IntentFilter,才会放行该Intent。
启动Activity时,会为Intent设置默认的Category,即CATEGORY_DEFAULT。目标Activity需要添加一个category为 CATEGORY_DEFAULT的IntentFilter来匹配这一类隐式的Intent。- Data:每一个IntentFilter可以定义零个或多个,数据可以通过类型(MIMEType)和位置(URI)来描述,如果Intent想要通过这个IntentFilter,则Intent所辖的Data需要匹配其中至少一个。
2.2 Component内部类
通过前面的博客总结我么可知,一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。在具体捯饬相关组件前,我们先来看下高层的基类Component组件它是怎么设计并且用代码实现的。
无需多言,直接开撸源码!
// 【 PackageParser.java 】
public class PackageParser {
...
public static class Component<II extends IntentInfo> {
public final Package owner;// 该组件依赖的包
public final ArrayList<II> intents;// 该组件所包含的IntentFilter
public final String className;// 组件的类名
public Bundle metaData;// 组件的元数据
ComponentName componentName;// 组件信息(组件所在的包名和类)
String componentShortName;// 组件简称
}
...
}
Component被设计成为一个基类,是Android包组件的众多组件的基类数据结构类,然后各个组件类继承并扩展它。并且Android包中每一个具体的组件所依赖的IntentInfo不同,所以Component和IntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。最终通过我们如上一番分析,我们会得到如下的类图:
PKMS启动详解(四):深入解析Android包解析器PackageParser

本文详细剖析了Android包解析器PackageParser的设计思想、核心成员变量和关键方法,展示了如何通过这些工具构建Android包信息。





最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



