PackageManagerService启动详解(四)之Android包信息体和解析器(上)

     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启动详解(三)之BOOT_PROGRESS_PMS_START流程分析以及更前面系列博客的耕耘,我们对PKMS启动流程有了一个整体的概括和图谱。并且经过BOOT_PROGRESS_PMS_START启动阶段一战PKMS通过Settings数据结构类完成了对已安装应用信息的加载和管控,并且也同时借助SystemConfig数据结构类完成了对Android相关系统配置信息完成了相关的加载和管控。

按照代码逻辑的发展,PKMS要开始对系统应用路径进行相关扫描然后解析应用安装包了,但是这里我们先不急于进行这一阶段的源码分析,因为我觉得这里有必要站在Android系统设计者的角度出发先来看看Android的设计者是通过什么策略对Android安装包进行管理,或者说是通过什么数据结构来管控Android安装包的(即apk应用的),只要我们掌握了Android设计者对应用包管理的设计思路,我想对于PKMS扫描应用安装目录的相关逻辑处理应该就是手到擒来的事情了。

注意:本篇的介绍是基于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
	ComponentInfo.java
	PermissionGroupInfo.java
	ApplicationInfo.java
	PermissionInfo.java
	PackageInfo.java
	ActivityInfo.java
	ServiceInfo.java
	ProviderInfo.java
	PermissionGroupInfo.java
	PackageItemInfo.java

这里必须郑重申明,郑重申明,郑重申明!

后续的前三个章节的博客绝大部分是基于博客Android包管理机制而来(加上自己的一些理解和思考),为啥我要冒着说抄袭的罪名也要把它放上来!因为他写的太好了,忍不住的把他放了进来,他的博客真的不错,这里不是打广告或者吹捧,他的有些博客确实写的很好,必须承认,值得我们学习!




一.站在上帝角度理解Android包管机制

让我们一起开启上帝视角看看Android的设计者是怎么设计Android的包管理机制的,学会了它我们就成了站在上帝上面的人了。

  有人的地方就有江湖,小到家庭,大到国家每一个社会群落都有管理机制,牵涉到管理那么就绕不开其中的三个要素:被管理者、管理者以及管理机制的运转。在Android的世界中,有一处群落叫“包管理”,要研究Android的包管理机制,同样可以从以下几个角度来思考:

  • 被管理的对象是什么?
  • 管理者的职能是什么?
  • 管理机制是如何运转的?

所谓Android包,其实就是一种文件格式,譬如APK包、JAR包等。在Android中存活着很多包,所有的应用程序都是APK包,很多构成Android运行环境的都是JAR包,还有一些以so为后缀的库文件,包管理者很重要的一个职能就是识别不同的包,统一维护这些包的信息。当有一个包进入或离开Android世界,都需要向包管理者申报,其他管理部门要获取包的具体信息,也都需要向包管理者申请。

如同社会是由人与人的协作形成,不同的包之间也需要进行协作。既然有协作,自然就有协作的规范,一个包可以干什么,不可以干什么,都需要有一个明确的范围界定,这就是包管理中的权限设计。涉及到的内容非常广泛,Linux的UGO(User Group Other)和ACL(Access Control List,访问控制列表)权限管理、数字签名与验证、Android授权机制、Selinux,都是包管理中权限设计的组成部分。

Android的世界就如同一个井然有序的人类社会,除了包管理部门,还有其他各种管理部门,譬如电源管理、窗口管理、活动管理等等,大家不仅各司其职,而且也有交流往来。从APK的安装到Activity的显示这么一个看似简单的过程,却需要大量管理部门参与进来,不断地进行数据解析、封装、传递、呈现,内部机理十分复杂。

对于上述三者关系中的管理者和被管理者,读者朋友肯定能很欢快的回答出来是PKMS和Android包,但是对于其中涉及的管理机制估计没有深入过PKMS的读者就很难回答上一二了!

这个没有关系,不熟悉才有学习的必要吗!在接下来的章节中,我们将会重点分析Android包的管理机制。




二.Android包形态的简介

对于PKMS机制中被管理者Android包,这里就介绍上了。

  Android中的APK和JAR包都以静态文件的形式分布在不同的硬件分区,包管理者面临的第一个任务就是将这些静态的文件转化成内存的数据结构,这样才能将其管理起来。Android中最重要的包管理对象就是APK,APK可以包含so文件,负责将静态文件转换内存中数据结构的工具就是PackageParser,包解析器(这里终于扣住我们要的主题了)。

在这里插入图片描述


Android L(5.0)以后,支持APK拆分,即一个APK可以分割成很多部分,位于相同的目录下,每一个部分都是一个单独的APK文件,所有的APK文件具备相同的签名,在APK解析过程中,会将拆分的APK重新组合成内存中的一个Package。对于一个完整的APK,Android称其为Monolithic;对于拆分后的APK,Android称其为Cluster

在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/即可,这样在包解析时,就会变成以Cluster的方式解析目录。

一个包在内存中的数据结构就是Package,那么,Package有一些什么属性?是怎么从APK文件中获取数据的呢? 这就涉及到包解析器的工作原理。Android的包管理者PKMS正是通过包管理解析器对Android包进行管理的!


2.1 Android包内部结构简介

  通过前面的介绍我们可以Android包的核心各种APK文件(对于jar包,这里我们不重点讨论),因为Android包管理机制的核心就是对Android包通过包管理解析器对其进行管理,所以这里我们非常有必要了解一下APK的内部构造,我们以zip文件的形式对APK展开看看其内部结构如下:

在这里插入图片描述


并且它的目录结构每个核心文件/目录功能如下:

APK内部文件或目录作用
META-INF/也就是一个 manifest ,从 java jar 文件引入的描述包信息的目录
res/资源文件目录
libs/如果存在的话,存放的是 ndk 编出来的 so 库
AndroidManifest.xmlAndroid程序全局配置文件
classes.dexdalvik 字节码
resources.ars编译后的二进制资源文件


三.Android包管理机制设计思路

  通过前面的章节我们对于Android包的管理者,以及被管理者Android包都有了一个初步的认识!那么将管理者和被管理者关联起来的就只有通过管理机制了,而这里的管理机制就是通过Android的包解析器了。


3.1 包解析器(PackageParse)设计思路

  通过为了先让读者对被管理对象有一个初步的认识,我们先把一个包最终在内存中的数据结构拎出来。其实生成这个数据结构,需要包管理者进行大量的调度工作,调度中心是PMS,包解析的过程也都是由PMS驱动的。在分析包解析过程之前,我们先上包解析的结果:

在这里插入图片描述


这个类图,示意了一个包最终在内存中的数据结构Package,它包含很多属性,部分属性还是包解析器中的子数据结构。我们可以从设计的角度来理解这个类图:

  • 一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。什么是组件呢?AndroidManifest.xml文件中所定义的的一些标签,就是组件,譬如<activity>,<service>,<provider>,<permission>等,这些标签分别对应到包解析器中的一个数据结构,它们各自有自身的属性。

  • 诸如<activity>,<service>标签,都可以配置<intent-filter>,来过滤其可以接收的Intent,这些信息也需要在包解析器中体现出来,为此组件Component依赖于IntentInfo这个数据结构。每一个具体的组件所依赖的IntentInfo不同,所以ComponentIntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。

  • 各种组件最终聚合到Package这个数据结构中,形成了最终包解析器的输出。当然,在解析的过程中,还有利用了一些数据结构来优化设计,PackageLiteApkLite就是一些很简单的数据封装。

要得到以上的数据结构,包解析器PackageParser功不可没,从接收一个静态的文件(File类型)开始,会经过一个漫长的包解析过程,直到生成最终的Package

parsePackages(File file...)
└── parseClusterPackage(File packageDir...)
    └── parseClusterPackageLite(File packageDir...)
    |   └── parseApkLite(File apkFile...)
    |       └── parseApkLite(String codePath...)
    └── parseBaseApk()
        └── parseBaseApplication()
        |    └── parseActivity()
        |    └── parseService()
        |    └── ...
        └── parseInstrumentation()
        └── ...

这些方法的具体逻辑本文不予分析,仅把关键的流程捋出来:

  • PackageParser.parsePackages()是包解析器的入口方法,它首先会判定给定的输入是否为一个目录,如果是目录,则以为着目录下可能存在多个拆分后的APK,这就需要以Cluster的方式进行解析;如果仅仅是一个APK文件,就以Monolithic的方式解析;
  • 解析APK,需要先得到一个中间数据结构PacakgeLite,包名、版本、拆分包等信息都会保存在这个数据结构中;由于一个包可能有多个拆分的APK,所以PackageLite可能关联到多个APK,每一个APK都对应到ApkLite这个数据结构,也是一些基本信息的封装。之所以以Lite为后缀命名,是因为这两个数据结构都比较轻量,只保存APK中很少信息;
  • 一个APK真正的信息都写在AndroidManifest.xml这个文件中,PackageParser.parseBaseApk()这个方法就是用来解析该文件。其解析过程与AndroidManifest.xml的文件结构一一对应,譬如先解析<application>标签的内容,然后解析其下的<activity>,<service>等标签。由于AndroidManifest.xml文件的结构非常复杂,所以该方法逻辑也非常庞大,读者们可以自行分析源码。

至此,包解析器PackageParser就将一个静态的文件,转换成了内存中的数据结构Package,它包含了一个包的所有信息,如包名、包路径、权限、四大组件等,其数据来源主要就是AndroidManifest.xml文件。


3.2 Android包信息体设计思路

  包解析器从静态文件中获取的数据,很多都是需要用于跨进程传递的,譬如初次启动Activity时,就需要把包信息从系统进程传递到应用进程,先完成应用进程的启动。在包解析器的类图中,我们看到Activity、Service、Provider、Permission、Instrumentaion这些类都有一个共同的特征:都具备info这个属性,其实这些类的结构非常简单,就是对info的一次封装,info这个结构体才是真正的包数据,笔者暂且称之为“包信息体”:

在这里插入图片描述


所有的info都实现了Parcelable接口,意图很明显,info是可以进行跨进程传递的。不同组件的info类型是不同的,除了实现了Parcelable接口,它们之间又构成了一个庞大的数据结构,把这些具体的info类型展开,就是以下的类图:

在这里插入图片描述


可以看到,这个类图与PackageParser中的类图在结构上很相似,我们依旧是从设计的角度来理解这个类图:

  • PackageItemInfo作为包每一项信息的高层基类:

    • 针对permissionpermissioninstrumentation等,分别为其设计了一个类,都继承自PackageItemInfo
    • 针对activityserviceprovider等四大组件,在PackageItemInfo之下又多设计了一层:ComponentInfo作为四大组件的基类
    • ApplicationInfo也是包信息中的一项,但与四大组件紧密相连,四大组件肯定都属于某个Application,所以ComponentInfoApplication存在依赖关系,继而,具体到每个组件都与Application存在依赖关系
  • 所有的包信息都聚合到PackageInfo这个类中,PackageInfo就是一个包向外提供的所有信息。其实除了上图列出来的类,还有一些类没有示意出来,譬如ConfigurationInfo,FeatureInfo,它们都可以对应到AndroidManifest.xml中的标签。

这些结构体中的数据,都是在包解析器时初始化的,譬如Activity依赖于ActivityInfo,在解析Activity时,就会创建一个ActivityInfo对象,把<activity>所定义的数据全都填充到ActivityInfo中。读者可以思考一下PackageParser中的Activity与此处的ActivityInfo的分开设计的目的和好处是什么?

关于此处我们需要重点注意如下几点

1、上述针对activityserviceprovider等四大组件,并不是我们通常意义上的Activity.java类,这里指代的是PackageParser的内部类千万不要搞混淆了。

2.并且在后续的分析中,我们会见到很多类,类的命名方式还有点相似,初读代码的时候,很容易陷入各个类之间复杂的关系网之中。不得不说,包在内存中的数据结构是比较庞大的,因为它蕴含的信息大多了。


3.3 关于包解析器和包信息体小结

  前面牛逼的博主已经为我们从整体方向和高层次的视角概括了Android包管理机制中核心的包解析器和包信息体的设计思路,不知道读者有没有get到其本质。这里为了使读者能更加的对上述二者有深层次的认识,这里我加上自己对上述二者的理解。

  • PackageParser包解析器
    对于它,我们可以理解为它是PKMS在安装,扫描应用过程中一个必不可少的工具类,它负责解析安装包,将解析的安装包信息用来添加数据结构类Package!然后Package有许多的成员变量又分别指向安装包中各个构成,譬如Service,Activity等,而这些类都有一个共同的特征:都具备info这个属性,而这个属性就是我们的包信息体相关的数据结构。并且PackageParser是害羞的,它是一个hide类不能被外部使用,从而决定了它只能负责相关的解析,确不会抛头露面对外提供接口
  • 包信息体
    通过PackageParser解析完Android包后,在PKMS中就会有它相关的数据记录,而此时第三方应用就可以通过public PackageInfo getPackageInfo(String packageName, int flags, int userId)获取已经安装应用相关安装包的信息。如果说包解析器是队内的,那么包信息体就是对外的,

理解了上述二者之间的联系,我想读者在后续的分析中就会游刃有余了。最终二者巧妙的结合一起,就构成我们包管理机制的核心要素了,最终我们就得到了如下的 一张大的类图关系图:

在这里插入图片描述




四.从源码角度出发理解Android包信息体设计思路

前面的章节,前人为我们高瞻远瞩的指明了道路的方向!但是如果不自己切身实践,就领会不到他们的意图和角度了,所以在后续章节中我们从源码角度出发来领悟,参透一下!

按照正常的逻辑,我们应该先从源码角度分析包解析器(PackageParse)的实现,但是这里为了排版和篇幅的原因,先从源码角度出发理解Android包信息体设计理念,后面再来分析PackageParse的实现。

  通过前面的章节3.2我们知道了包解析器从静态文件中获取的数据,很多都是需要用于跨进程传递的,譬如初次启动Activity时,就需要把包信息从系统进程传递到应用进程,先完成应用进程的启动。在包解析器的类图中,我们看到Activity、Service、Provider、Permission、Instrumentaion这些类都有一个共同的特征:都具备info这个属性,其实这些类的结构非常简单,就是对info的一次封装,info这个结构体才是真正的包数据,笔者暂且称之为“包信息体”。这里我们就先从最外层的PackageItemInfo开始分析起!


4.1 PackageItemInfo类

对于PackageItemInfo我们将从如下几个方面来发起攻击:

  • PackageItemInfo类概述
  • PackageItemInfo类重要成员变量和方法
  • PackageItemInfo在Android包设计中的使用

好了,啥也不说了,先干为敬!

4.1.1 PackageItemInfo类概述

通常看人看脸,而看Android源码通常是看设计者着对它的概述了,我们这里看下Android设计者对它的概述定义如下:

// 【 PackageItemInfo.java 】
/**
 * Base class containing information common to all package items held by
 * the package manager.  This provides a very common basic set of attributes:
 * a label, icon, and meta-data.  This class is not intended
 * to be used by itself; it is simply here to share common definitions
 * between all items returned by the package manager.  As such, it does not
 * itself implement Parcelable, but does provide convenience methods to assist
 * in the implementation of Parcelable in subclasses.
 */

/**
       PackageItemInfo, 代表一个应用包内所有组件项以及通用信息的基类。该类提供最基本的属性集,
       如: label, icon, meta-data等,并且一般不会直接使用该类,它设计之初就是为了包内其它基本
       组件项提供统一的基本定义。它没有实现接口Parcelable, 但它提供了传Parcel型的构造函数,
       以及writeToParcel()方法给它的子类来实现PackageItemInfo内部这部分的成员的Parcel化。

*/
public class PackageItemInfo {
	...
}

英文比较好的先生们,女士们应该比较好容易理解上面的概述(英文不好的同仁们,就只能接收我蹩脚的中文翻译了,勿见怪)!上述对于PackageItemInfo一句话归纳总结就是它是作为"包每一项信息的高层基类",用来被继承实现的。

这里我们需要知道的是PackageItemInfo直接子类,有如下几个:
在这里插入图片描述

4.1.2 PackageItemInfo类主要成员变量

通常一个数据基类,最最重要的就是它的成员变量了,我们先睹为快,看看PackageItemInfo的庐山真面目!

// 【 PackageItemInfo.java 】

public class PackageItemInfo {

	/*
		表示包组件项的名称,从"android:name"属性得到
		注意是组件项的名称,而不是Android包的名称
	*/	
    public String name;
    
	// 该组件项所在Android包的包名
    public String packageName;
    
	/*
		指向该组件项的标签,为String型的资源id,
		从"android:label"属性得到,如不设置则为0.
	*/
    public int labelRes;
    
    /**
     * The string provided in the AndroidManifest file, if any.  You
     * probably don't want to use this.  You probably want
     * {@link PackageManager#getApplicationLabel}
     */
     // 这个应该指向的是Application对应的Label
    public CharSequence nonLocalizedLabel;
    
	/*
		指向该组件项的图标,drawable型的资源id,
		从"android:icon"属性得到,如不设置则为0.
	*/
    public int icon;
    
	/*
		指向该组件项的横幅,drawable型的资源id,
		从"android:banner"属性得到,如不设置则为0.
	*/
    public int banner;

	/*
		指定该组件项的logo,drawable型的资源id,比应用图标要大,
		ActionBar可以设置是否显示logo, 从"android:banner"属性得到,如不设置则为0
	*/
    public int logo;
    
	/*
		对应AndroidManifest中的<meta-data>标签。 
		只有<activity>,  <activity-alias>, <service>, <receiver>, <application>
		标签中可能包含<meta-data>子标签。
	*/
    public Bundle metaData;

	// 表示是否显示用户icon
    public int showUserIcon;
	...
}

上述信息填充的来源通常是AndroidManifest.xml文件下对应组件标签下的子标签,这个读者了解一下即可。

4.1.3 PackageItemInfo类主要方法

对于一个数据类型的基类,通常它的方法是用来提供获取/设置成员变量信息的,这里的PackageItemInfo是不是也遵循这一套路呢,这里我们透过源码来看实质,如下:

// 【 PackageItemInfo.java 】

public class PackageItemInfo {
	// 各种花式构造方法
    public PackageItemInfo();
    public PackageItemInfo(PackageItemInfo orig);
    protected PackageItemInfo(Parcel source);

	/*
		顾名思义,返回该组件项的标签,优先级依次为:nonLocalizedLabel, labelRes, name, packageName.
	*/
	public CharSequence loadLabel(PackageManager pm);

	/*
		返回该组件项的图标,通过ApplicationPackageManager的loadItemIcon()方法获取icon对应的Drawable.
	*/
	public Drawable loadIcon(PackageManager pm)

	/*
		返回该组件项的横幅,通过ApplicationPackageManager的getDrawable()
		方法获取banner对应的Drawable, 如果banner为0, 返回loadDefaultBanner()的结果.
	*/

	public Drawable loadBanner(PackageManager pm)

	/*
		返回该组件项的大图标,通过ApplicationPackageManager的getDrawable()
		方法获取logo对应的Drawable, 如果logo为0, 返回loadDefaultLogo()的结果.	
	*/
	public Drawable loadLogo(PackageManager pm)

	/*
		返回该组件的默认图标,通过ApplicationPackageManager的getDefaultActivityIcon()方法, 
		返回的结果是com.android.internal.R.drawable.sym_def_app_icon对应的Drawable.
	*/
	public Drawable loadDefaultIcon(PackageManager pm)

	// 返回null
	protected Drawable loadDefaultBanner(PackageManager pm)
	// 返回null
	protected Drawable loadDefaultLogo(PackageManager pm)

	/*
		找到metaData中对应为name的资源id, 通过ApplicationPackageManager的getXml()方法, 
		返回该id对应的XmlResourceParser.
	*/
	public XmlResourceParser loadXmlMetaData(PackageManager pm, String name)

	// 返回null
	protected ApplicationInfo getApplicationInfo()

	// 为PackageItemInfo的子类Parcel化提供基类部分的成员的写入
	public void writeToParcel(Parcel dest, int parcelableFlags)
}

看来PackageItemInfo也被拜托老套路啊!

4.1.4 PackageItemInfo在Android包管理中的使用

通过搜寻发现在解析Android包的过程中只有PackageParser.parsePackageItemInfo方法会以out参数的形式获取PackageItemInfo的信息,其定义如下:

// 【 PackageParser.java 】
    private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
            String[] outError, String tag, TypedArray sa, boolean nameRequired,
            int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes);

这里可以看到parsePackageItemInfo方法是PackageParser的私有方法,也是唯一的参数有PackageItemInfo的方法,它在PackageParser中三个地方被调用,分别用于解析标签 <permission-group>生成 PermissionGroupInfo, 解析 <permission>生成PermissionInfo, 解析<permission-tree>生成PermissionInfo。

所以可以看到PackageItemInfo类在Android包管理机制中只有极少部分时候被直接使用,更多的是它的子类、间接子类,不过它定义了组件的共同特征,为研究其它组件信息提供基础,组件信息均是public的,外部直接用该类实例来引用赋值。并且我们通过前面总结可知它的直接子类是ComponentInfo, ApplicationInfo,这个我们会在后面继续介绍。

4.1.5 PackageItemInfo类小结

至此PackageItemInfo类我们就介绍完毕了,对于它在Android包管理中的定位和功能我们也应该心里大概有谱了,还是一句话总结它定义了Android包组件的共同特性,是用来被继承和实现的。最后附上它的类图,如下:

在这里插入图片描述


4.2 ComponentInfo类

PackageItemInfo分析完毕,我们来说说其子类ComponentInfo,老规矩,我们将从如下几个方面来发起攻击:

  • ComponentInfo类概述
  • ComponentInfo类重要成员变量和方法
  • ComponentInfo在Android包设计中的使用

好了,啥也不说了,先干为敬!

4.2.1 ComponentInfo概述

多余的文字我也给省了,直接看设计者对它的定义:

// 【 ComponentInfo.java 】
/**
 * Base class containing information common to all application components
 * ({@link ActivityInfo}, {@link ServiceInfo}).  This class is not intended
 * to be used by itself; it is simply here to share common definitions
 * between all application components.  As such, it does not itself
 * implement Parcelable, but does provide convenience methods to assist
 * in the implementation of Parcelable in subclasses.
 */
 
/*
	ComponentInfo, 代表一个应用内组件(如ActivityInfo, ServiceInfo, ProviderInfo)通用信息的基类。
	一般不会直接使用该类,它设计是为了不同应用的组件共享统一的定义。它没有实现接口Parcelable, 
	但它提供了传Parcel型的构造函数,以及writeToParcel()方法给它的子类来实现ComponentInfo内部这部分的成员的Parcel化

*/
public class ComponentInfo extends PackageItemInfo {
	...
}

对于ComponentInfo我用一句话概括总结就是它是对PackageItemInfo的具象化,它是ActivityInfo, ServiceInfo, ProviderInfo基类,用来抽象化Android包组件中上述信息。

ComponentInfo的直接子类有且仅有上述三个!

4.2.2 ComponentInfo类主要成员变量和方法

前面感觉介绍PackageItemInfo有点太啰嗦了,这里我们就不对ComponentInfo过度的展开了,只查看其成员变量,方法基本是对成员变量的操作。

直接上源码,来看看ComponentInfo类的真面目!

// 【 ComponentInfo.java 】

public class ComponentInfo extends PackageItemInfo {
	
	/*
		表示该组件所在的包的application信息,从<application>标签获取
	*/
    public ApplicationInfo applicationInfo;
    
	/*
		该组件所运行的进程名,string型,从"android:process"属性得到,
		如不设置则为applicationInfo.processName.
	*/
    public String processName;

	/*
		该组件的描述,string型的资源id,从"android:description"属性得到,如不设置则为0
	*/
    public int descriptionRes;
    
	
	/*
		表明当前组件能否被实例化,boolean型,从"android:enabled"属性得到。
		如果它所在的ApplicationInfo中enabled为false, 则这处的设置无效
	*/
    public boolean enabled = true;

	/*
		表明当前组件能否被其它Application的组件启动,boolean型,从"android:exported"属性得到。
        如果当前组件没有一个<intent-filter>则它默认为false(没有任何<intent-filter>表明要用组件的准确名称来启动), 
        
        exported=false表明当前组件只能被当前应用内组件启动,或有相同UID的应用。
           
	*/
    public boolean exported = false;

	...
}

4.2.3 ComponentInfo在Android包管理中的使用

通过搜寻发现在解析Android包的过程中只有PackageParser.Component内部类会以out参数的形式获取ComponentInfo的信息,其定义如下:

// 【 PackageParser.java 】
public static class Component<II extends IntentInfo> {
	public Component(final ParseComponentArgs args, final ComponentInfo outInfo) {
	}
}

这里可以看到PackageParser中的组件基类Component,它的一个构造函数会传入ComponentInfo的类, 内部对它的成员进行赋值。

4.2.4 ComponentInfo类小结

至此ComponentInfo类我们就介绍完毕了,对于它在Android包管理中的定位和功能我们也应该心里大概有谱了,还是一句话总结,它是PackageItemInfo基类的具象化,用来表述Android包组件Service,Activity,Provider信息的。最后附上它的类图,如下:

在这里插入图片描述


4.3 ApplicationInfo类

ApplicationInfo分析完毕,我们来说说其子类ApplicationInfo,老规矩,我们将从如下几个方面来发起攻击:

  • ApplicationInfo类概述
  • ApplicationInfo类重要成员变量和方法
  • ApplicationInfo在Android包设计中的使用

好了,啥也不说了,先干为敬!

4.3.1 ApplicationInfo类概述

多余的文字我也给省了,直接看设计者对它的定义:

/**
 * Information you can retrieve about a particular application.  This
 * corresponds to information collected from the AndroidManifest.xml's
 * &lt;application&gt; tag.
 */

/*
	通过它可以得到一个应用基本信息。这些信息是T通过解析应用的AndroidManifest.xml
	的<application>标签获取的
*/
public class ApplicationInfo extends PackageItemInfo implements Parcelable {
	...
}

对于ApplicationInfo我用一句话概括总结,就是它是对PackageItemInfo的具象化,它是Android包中AndroidManifest.xml中标签的数据实例化类。

4.3.2 ApplicationInfo类主要成员和方法

直接上源码,来看看ComponentInfo类的真面目!

// 【 ApplicationInfo.java 】
public class ApplicationInfo extends PackageItemInfo implements Parcelable {
    
	/*
		当前应用所有Activity的默认task密切性。从”android:taskAffinity“属性得到
	*
    public String taskAffinity;
    
	/*
		可选项,访问当前应用所有组件需要声明的权限,从”android:permission“属性得到
	*/
    public String permission;
    
	/*
		从”android:process“属性得到,表明应用运行的进程名。或不设置则默认为应用包名
	*/
    public String processName;

	/*
		 从”android:class“属性得到,应用实现的Application类的类名
		 如果不设置则使用默认的名称""
	*/
    public String className;
    
	/ *
		对Application组件的描述,从”android:description“属性得到,若不设置则为0
	*/
    public int descriptionRes;    
    
	/*
		指向应用的主题,从”android:theme“属性得到,。若不设置则为0
	*/
    public int theme;

	/*
			从”android:manageSpaceActivity“属性得到,用于指定一个Activity来管理数据,
			它最终会出现在设置->应用程序管理中,默认为按钮为”清除数据”,指定此属性后,
			该按钮可点击跳转到该Activity, 让用户选择性清除哪些数据。若不设置则为null.
	*/
	public String manageSpaceActivityName; 

	
	/*
			从”android:backupAgent“属性得到,Android原生的备份引擎BackupManagerService在应用端的实现类,
			是backupAgent的子类。默认不会由系统备份,若”android:allowBackup“值为false, 则该属性设置无效。
	
	*/
	public String backupAgentName; 

	/*
		可选项,注明应用是否支持自动备份。
	*/
	public int fullBackupContent = 0; 

	/*
		为应用内所有Activity设置的默认UI选项,可选值为"none", "splitActionBarWhenNarrow"。
	*/
	public int uiOptions = 0; 

	/*
		flag标志位,常见的有android:debuggable和android:persistent等配置选项
	*/
	public int flags = 0; 

	/*
		来自SELinux策略中seinfo标签,这个值一般在设置应用进程的SELinux安全上下文时有用
	*/
	public String seinfo; 

	// 应用的数据目录
	public String dataDir; 

	// JNI本地库存放路径
	public String nativeLibraryDir; 
	...

	// 构造方法
	public ApplicationInfo(...);

	public void writeToParcel(Parcel dest, int parcelableFlags);
	
	// 通过return this返回当前ApplicationInfo实例
	protected ApplicationInfo getApplicationInfo();

	// 判断当前应用是否为系统应用。flags与ApplicationInfo.FLAG_SYSTEM按位与不等于0则返回true
	public boolean isSystemApp()

这里可以看到ApplicationInfo类的成员变量和方法还是比较多的,这里就不展开了!读者可以在具体使用的时候结合源码一起查看,最好是对照AndroidManifest.xml文件!

4.3.3 ApplicationInfo在Android包管理中的使用

关于此处ApplicationInfo在Android包管理中使用的情况,比较复杂,总之它会通过PackageParser解析Android包的过程中被赋值,这个在后面的博客中再细说。


4.4 包信息体牵涉的其它类

包信息体其它相关的类PermissionInfo,InstrumentationInfo,PermissionGroupInfo,PackageInfo这里就不过多展开了,感兴趣的读者可以自行分析。总之我们可以认为PackageInfo是包信息体主根,而PermissionInfo,InstrumentationInfo,PermissionGroupInfo,PackageInfo等都是它的字根是对PackageInfo的填充而已。最后关于包信息体,我们可以得到如下的树形结构图:

>




总结

  好了,到这里PackageManagerService启动详解(四)之Android包信息体和解析器(上)分析就告一段落了,在本篇博客中我们重点阐述了如下知识点:

  • Android包管理机制中的Android包信息体设计思想和源码逻辑
  • Android包管理机制中的Android包解析器设计思想

但是由于篇幅和包解析器源码逻辑过于繁琐,就将其留到下一个章节进行相关的分析了。

好了,各位青山不改绿水长流,各位江湖见!当然各位读者的点赞和关注是我写作路上前进的最大动力了,如果有啥不对或者不爽的也可以踩一踩也无妨!

评论 17 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

IT先森

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值