AndroidManifest解析

一.简介

AndroidManifest.xml文件在Android中我们称为清单文件。包含了应用的包名、权限、四大组件等信息。

一般我们直接将APK修改格式为zip后解压缩生成的AndroidManifest.xml打开(比如用Notepad)会是一堆乱码。是因为在APP打包的过程中,清单文件被编译成了二进制数据存储在安装包中。

所以,这里我们就来研究下AndroidManifest.xml的二进制文件结构,这样我们才可以获取到我们需要的信息。

(当然有一些开源工具,比如apktool、AXmlPrinter等可以直接读取编译后的AndroidManifest文件)

附上一张经典的结构图:

AndroidManifest解析

下面我们以WhatsApp的AndroidManifest.xml文件为例进行分析。

二.AndroidManifest.xml文件格式解析

我们使用010 Edit打开WhatsApp的AndroidManifest.xml后,如下图:

AndroidManifest解析

结合之前的结构图,AndroidManifest.xml的总体结构大致可以分为:

1.HEADER header

2.STRINGCHUNK stringChunk

3.RESOURCEIDCHUNK resourceChunk

4.XMLCONTENT CHUNK XmlContent Chunk

逐个来看!

1.头部(header)

AndroidManifest解析

我们可以看到header包括了2个magicnumber(魔数)和filesize(文件大小)。各占了4个字节。

A.magicnumber

其中magicnumber始终为0x000080003。

B.filesize

filesize是指的文件总字节数,我们这里大小为0x0001747C,对应大约是95356字节(93KB左右),可以看下我们的AndroidManifest文件大小也大约是这个值。

AndroidManifest解析

2.字符串资源池(String Chunk)

AndroidManifest解析

从0x00000008开始为String Chunk的内容,String Chunk 主要存储了AndroidManifest文件中的所有字符串信息。

A.scSignature

这是String Chunk的标识。占4个字节,并且也是固定的:0x001C0001

B.scSize

定义String Chunk的大小。也是占4个字节。这里的值是0x00009C18(即:39960 bytes)。

所以整个String Chunk的内容区间就是0x00000008 到 0x0009C16h

C.scStringCount

定义的是字符串的个数。同样是4个字节。这里是0x00000204,即一共有516个字符串。

D.scStyleCount

定义的是样式的数量。占4个字节。这里是为0x0000000。

E.scUNKNOW

UNKNOW值,固定值且占4个字节。

F.scStringPoolOffset

字符串池的偏移量,这里是相对于String Chunk的开始处。

G.scStylePoolOffset

样式池的偏移量,上面我们的杨树数量scStyleCount为0,所以这里的偏移量为0x00000000。

H.scStringOffsets[]

int数组,大小就是scStringCount的值 516。

I.strItem[]

字符串池中的每个字符串的格式。

AndroidManifest解析

a.sfSize

字符串的长度。这里是0x00000005,即为5。这个长度指的是字符串中字符的个数,并非字节数。

b.ONECHAR[5]

字符串的内容,每个字符对应两个字节,该字符串共有5个字符,所以一起是10字节。

我们这里来看第一个字符:0x0074,对应的二进制值为116 (A:97 ),所以对应的字母为“t”

结合在一起,字符串的内容就是theme。

c.sfEnd

字符串终止符。这里为0x0000。

3.系统资源Id(ResourceId Chunk)

AndroidManifest解析

a.rcSignature

ResourceId Chunk的标识符。4个字节。值是固定的,为0x00080180

b.rcSize

ResourceId Chunk的大小。占4个字节。值为0x000000C8,即为200 bytes

c.rcItem[]

int数组,它的大小为 (rcSize-8)/4 。这里为48。

这里的ResourceId对应的是源码中的/frameworks/base/core/res/res/values/public.xml

我们看其中某一个rcItem:

AndroidManifest解析

可以看到实际上是和我们源码中的public.xml一一对应的。

4.AndroidManifest的具体信息(XML Content)

XML Content一共有五种类型的Chunk,分别是StartNamespaceChunk、startTagChunk、endTagChunk、endNamespaceChunk、TextChunk。

A.startNamespaceChunk

AndroidManifest解析

a.sncSignature

该Chunk的标识符。4个字节。固定值为0x00100100。

b.sncSize

Chunk的大小,4个字节。这里值为0x00000018,即为24 bytes。

c.sncLineNumber

Chunk的行号。占4个字节。值为0x00000002。对应的是编码前文件的第2行的内容。

即是如下:

<manifest xmlns:android=“http://schemas.android.com/apk/res/android”

d.sncPreflix

索引值,占4个字节。指向的是字符串池对应的字符串,表示命名空间的前缀。

我们这里的值是0x0000003D,即为61。这时我们看String Pool的索引也就是strItem[61]的值,就是“android”

AndroidManifest解析

e.sncUri

同样是指向String Pool中对应索引的字符串,表示命名空间的Uri。

我们这里的值是0x000001E3,即为483。也是去看strItem[483]的值,是为“http://schemas.android.com/apk/res/android”

AndroidManifest解析

B.endNamespaceChunk

其中的字段不详细说了,同StartNamespaceChunk类似。

AndroidManifest解析

C.startTagChunk

AndroidManifest解析

a.stcSignature

startTagChunk的标识符,4个字节且值唯一,为0x00100102

b.stcSize

startTagChunk的大小,占4个字节。值为0x00000074(即为116)

c.stcLineNumber

startTagChunk的行号,占4个字节。值为0x000006A8(即为1704)

d.stcName

标签名称在String Pool中的索引。这里值为0x000001F0(即为496),对应的strItem[496]的值,为“provider”

AndroidManifest解析

e.stcFlags

一个固定值:0x00140014,暂未知作用。

f.stcAttributeCount

属性个数。占4个字节。这里的值为:0x00000004(即为4)

g.attributeChunk

属性内容。以其中一个为例,先来看构造:

AndroidManifest解析

可以看到这个chunk占20 字节,有5个字段。

acNamespaceUri:属性的命名空间uri在String Pool中的索引。我们这里是0x000001E3(即为483)。对应的strItem[483]的值为“http://schemas.android.com/apk/res/android“

AndroidManifest解析

acName:属性名称在String Pool中的索引。这里值为0x00000003(即为3),对应的strItem[3]的值为”name“

AndroidManifest解析

acValueStr:属性值。也是需要对应的看String Pool中的索引。这里值为0x00000096(即为150)。对应的strItem[150]的值为”androidx.lifecycle.ProcessLifecycleOwnerInitializer“

AndroidManifest解析

acType:属性类型

acData:属性数据

所以。这个最终的startTagChunk就是如下:

D.endTagChunk

和startTagChunk类似的。每一对TagChunk都是对应的,有start也肯定有end的。

rovider android:name=“androidx.lifecycle.ProcessLifecycleOwnerInitializer” android:exported=“false” android:multiprocess=“true” android:authorities=“com.whatsapp.lifecycle-process”/>

D.endTagChunk

和startTagChunk类似的。每一对TagChunk都是对应的,有start也肯定有end的。

AndroidManifest解析