盒子
盒子
文章目录
  1. RES_TABLE_TYPE_SPEC_TYPE
  2. RES_TABLE_TYPE_TYPE
    1. ResTable_entry
    2. ResTable_map_entry
      1. 资源的id
      2. ResTable_map
  3. Demo

可能是全网讲最细的安卓resources.arsc解析教程(二)

上篇博客写到,Package资源剩下的部分是由多组RES_TABLE_TYPE_SPEC_TYPE和RES_TABLE_TYPE_TYPE构成的。

一个RES_TABLE_TYPE_SPEC_TYPE后面跟着一个或者多个RES_TABLE_TYPE_TYPE构成一种类型的资源的描述(例如string类型、bool类型、dimen类型等)

RES_TABLE_TYPE_SPEC_TYPE

我们接着来看看RES_TABLE_TYPE_SPEC_TYPE的头部结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* A specification of the resources defined by a particular type.
*
* There should be one of these chunks for each resource type.
*
* This structure is followed by an array of integers providing the set of
* configuration change flags (ResTable_config::CONFIG_*) that have multiple
* resources for that configuration. In addition, the high bit is set if that
* resource has been made public.
*/
struct ResTable_typeSpec
{
struct ResChunk_header header;

// The type identifier this chunk is holding. Type IDs start
// at 1 (corresponding to the value of the type bits in a
// resource identifier). 0 is invalid.
uint8_t id;

// Must be 0.
uint8_t res0;
// Must be 0.
uint16_t res1;

// Number of uint32_t entry configuration masks that follow.
uint32_t entryCount;

enum {
// Additional flag indicating an entry is public.
SPEC_PUBLIC = 0x40000000
};
};

从注释中可以知道ResTable_typeSpec头部后面会跟着entryCount个uint32_t,代表这种类型有entryCount个数据,并且每个uint32_t标识了这个数据在哪些configuration下有特殊的值。

这些configuration可能是不同的地区、不同的屏幕分辨率、不同的sdk版本等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Flags indicating a set of config values.  These flag constants must
// match the corresponding ones in android.content.pm.ActivityInfo and
// attrs_manifest.xml.
enum {
CONFIG_MCC = ACONFIGURATION_MCC,
CONFIG_MNC = ACONFIGURATION_MCC,
CONFIG_LOCALE = ACONFIGURATION_LOCALE,
CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN,
CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD,
CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN,
CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION,
CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,
CONFIG_DENSITY = ACONFIGURATION_DENSITY,
CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE,
CONFIG_SMALLEST_SCREEN_SIZE = ACONFIGURATION_SMALLEST_SCREEN_SIZE,
CONFIG_VERSION = ACONFIGURATION_VERSION,
CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
};

这里直接举个例子,例如我们可能会在res/values目录下创建一些bool配置:

1
2
3
<bool name="abc_action_bar_embed_tabs">true</bool>
<bool name="abc_allow_stacked_button_bar">false</bool>
<bool name="abc_config_actionMenuItemAllCaps">true</bool>

然后可能在竖屏的情况下我们不需要显示action bar,所以在res/values-port目录下我们会把abc_action_bar_embed_tabs的值设置成false

1
<bool name="abc_action_bar_embed_tabs">false</bool>

然后下面代码就能在横屏、竖屏下拿到不同的配置了:

1
context.getResources().getBoolean(R.bool.abc_action_bar_embed_tabs);

在代码里面,我们可以先读取ResTable_typeSpec,然后根据entryCount得到这种类型有多少个数据(例如这里的bool就有abc_action_bar_embed_tabs、abc_allow_stacked_button_bar、abc_config_actionMenuItemAllCaps三个数据,所以bool类型下的entryCount就是3),然后继续读entryCount个uint32_t,读出来就是每个数据在哪些configuration下有特殊的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//printStringFromStringsPool:

void printStringFromStringsPool(uint32_t* pOffsets, char* pStringsStart, uint32_t stringIndex, uint32_t isUtf8) {
//前面两个字节是长度,要跳过
char* str = pStringsStart + *(pOffsets + stringIndex) + 2;
if(isUtf8) {
printf("%s\n", str);
} else {
printUtf16String((char16_t*)str);
}
}


//main:

...

ResTable_typeSpec typeSpecHeader;
uint32_t config;
uint16_t type;
while(fread((void*)&type, sizeof(u_int16_t), 1, pFile) != 0) {
fseek(pFile, -sizeof(uint16_t), SEEK_CUR);
if(RES_TABLE_TYPE_SPEC_TYPE == type) {
fread((void*)&typeSpecHeader, sizeof(struct ResTable_typeSpec), 1, pFile);
printf("type: id=0x%x,name=", typeSpecHeader.id);
printStringFromStringsPool(
(uint32_t*)pTypeStrings,
(char*)pTypeStrings + typeStringPoolHeader.stringsStart - sizeof(struct ResStringPool_header),
typeSpecHeader.id - 1,
typeStringPoolHeader.flags & ResStringPool_header::UTF8_FLAG
);

for(int i = 0 ; i < typeSpecHeader.entryCount ; i++) {
fread((void*)&config, sizeof(uint32_t), 1, pFile);
printf("%x\n",config);
}
}
...
}

...

我们直接找到bool类型下的打印:

1
2
3
4
5
6
7
...
type:id=3,name=bool
80
0
0

...

可以看到bool类型下的确有三个uint32_t,分别是80、0、0。这个80代表的就是CONFIG_ORIENTATION,也就是说这个数据在不同的屏幕方向下面会有和默认值不同的值。而0则代表了这个数据只有一个默认值,不会跟着configuration的变化而改变:

1
2
3
4
5
//configuration.h
ACONFIGURATION_ORIENTATION = 0x0080,

//ResTable_config里面的enum
CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,

让我们返回去对比下:

1
2
3
4
5
6
7
// res/values目录下
<bool name="abc_action_bar_embed_tabs">true</bool>
<bool name="abc_allow_stacked_button_bar">false</bool>
<bool name="abc_config_actionMenuItemAllCaps">true</bool>

// res/values-port目录下
<bool name="abc_action_bar_embed_tabs">false</bool>

第一个abc_action_bar_embed_tabs在不同的屏幕方向下可能值会改变,所以它的uint32_t值是80,也就是CONFIG_ORIENTATION,而abc_allow_stacked_button_bar 和abc_config_actionMenuItemAllCaps只有默认的配置,所以他们的uint32_t都是0。

所以RES_TABLE_TYPE_SPEC_TYPE的作用就是将数据受到哪些configuration影响都标识出来。

在读取数据的时候先看看它是否会受configuration影响,如果不会,直接读默认的RES_TABLE_TYPE_TYPE里面的默认值就好,否则就根据当前的configuration去到后面对应的RES_TABLE_TYPE_TYPE下面读取对应的值了。

RES_TABLE_TYPE_TYPE

讲的这里终于到了最重要的部分,我们在xml里面配的值,都会在RES_TABLE_TYPE_TYPE里面体现出来。

我们照例先来看看它的头部结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* A collection of resource entries for a particular resource data
* type. Followed by an array of uint32_t defining the resource
* values, corresponding to the array of type strings in the
* ResTable_package::typeStrings string block. Each of these hold an
* index from entriesStart; a value of NO_ENTRY means that entry is
* not defined.
*
* There may be multiple of these chunks for a particular resource type,
* supply different configuration variations for the resource values of
* that type.
*
* It would be nice to have an additional ordered index of entries, so
* we can do a binary search if trying to find a resource by string name.
*/
struct ResTable_type
{
struct ResChunk_header header;

enum {
NO_ENTRY = 0xFFFFFFFF
};

// The type identifier this chunk is holding. Type IDs start
// at 1 (corresponding to the value of the type bits in a
// resource identifier). 0 is invalid.
uint8_t id;

// Must be 0.
uint8_t res0;
// Must be 0.
uint16_t res1;

// Number of uint32_t entry indices that follow.
uint32_t entryCount;

// Offset from header where ResTable_entry data starts.
uint32_t entriesStart;

ResTable_config config;
};

这个ResTable_type里有个config成员,它就是具体的配置了,我们可以把它打印出来:

1
2
3
4
5
else if(RES_TABLE_TYPE_TYPE == type) {
fread((void*)&typeHeader, sizeof(struct ResTable_type), 1, pFile);
printConfig(typeHeader.config);
...
}

找到bool的那一段,可以看到它有两个RES_TABLE_TYPE_TYPE,第一个是默认的配置(values目录),第二个是port下的配置(values-port目录):

1
2
3
4
5
6
7
8
...
type: id=0x3,name=bool
80
0
0
config :
config : port
...

然后根据注释的说明我们知道,ResTable_type头部后跟着entryCount个uint32_t,代表了每个entry相对entriesStart的偏移。这里和RES_STRING_POOL_TYPE有点像,也是从偏移数组读取数据的偏移值,然后从entriesStart进行偏移得到数据的地址。

那entriesStart后面的entry是什么呢?其实entry有两种类型ResTable_entry和ResTable_map_entry。

他们其实是有继承关系的,ResTable_map_entry是ResTable_entry的子类(这里的继承关系是c++里面的继承关系,前面我们都是用c语言去讲的,但是这里必须引入c++了,不过也是最基础的继承而已,大家可以自行搜索下)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* This is the beginning of information about an entry in the resource
* table. It holds the reference to the name of this entry, and is
* immediately followed by one of:
* * A Res_value structure, if FLAG_COMPLEX is -not- set.
* * An array of ResTable_map structures, if FLAG_COMPLEX is set.
* These supply a set of name/value mappings of data.
*/
struct ResTable_entry
{
// Number of bytes in this structure.
uint16_t size;

enum {
// If set, this is a complex entry, holding a set of name/value
// mappings. It is followed by an array of ResTable_map structures.
FLAG_COMPLEX = 0x0001,
// If set, this resource has been declared public, so libraries
// are allowed to reference it.
FLAG_PUBLIC = 0x0002,
// If set, this is a weak resource and may be overriden by strong
// resources of the same name/type. This is only useful during
// linking with other resource tables.
FLAG_WEAK = 0x0004
};
uint16_t flags;

// Reference into ResTable_package::keyStrings identifying this entry.
struct ResStringPool_ref key;
};


/**
* Extended form of a ResTable_entry for map entries, defining a parent map
* resource from which to inherit values.
*/
struct ResTable_map_entry : public ResTable_entry
{
// Resource identifier of the parent mapping, or 0 if there is none.
ResTable_ref parent;
// Number of name/value pairs that follow for FLAG_COMPLEX.
uint32_t count;
};

看到注释我们可以知道, ResTable_entry有个flags成员变量,如果它的FLAG_COMPLEX位被置1(也就是说flags & 0x0001 != 0),则它是个ResTable_map_entry结构。

两种结构的不同之处在于ResTable_entry后面跟着的是一个Res_value,而ResTable_map_entry后面跟着的是多个name/value键值对,这个键值对是用struct ResTable_map来表示的。

ResTable_entry

我们先从ResTable_entry讲起,我们读完struct ResTable_type头部信息之后继续将offset数组和entriesStart开始到剩下的部分都读进去保存到pOffset和pData中。

接着就可以用*(pOffsets + i)得到每个entry的偏移,再与entriesStart相加得到entry的具体位置。这里有一点需要注意的是如果offset是ResTable_type::NO_ENTRY,也就是0xFFFFFFFF的时候,代表它是无效的,直接跳过即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
else if(RES_TABLE_TYPE_TYPE == type) {
fread((void*)&typeHeader, sizeof(struct ResTable_type), 1, pFile);
printConfig(typeHeader.config);

// 实际struct ResTable_type的大小可能不同sdk版本不一样,所以typeHeader.header.headerSize才是真正的头部大小
fseek(pFile, typeHeader.header.headerSize - sizeof(struct ResTable_type), SEEK_CUR);;

uint32_t* pOffsets = (uint32_t*)malloc(typeHeader.entryCount * sizeof(uint32_t));
fread((void*)pOffsets, sizeof(uint32_t), typeHeader.entryCount, pFile);

unsigned char* pData = (unsigned char*)malloc(typeHeader.header.size - typeHeader.entriesStart);
fread((void*)pData, typeHeader.header.size - typeHeader.entriesStart, 1, pFile);

for(int i = 0 ; i< typeHeader.entryCount ; i++) {
uint32_t offset = *(pOffsets + i);
if(offset == ResTable_type::NO_ENTRY) {
continue;
}
struct ResTable_entry* pEntry = (struct ResTable_entry*)(pData + offset);
printf("entryIndex: 0x%x, key :\n", i);
printStringFromStringsPool(
(uint32_t*)pKeyStrings,
(char*)pKeyStrings + keyStringPoolHeader.stringsStart - sizeof(struct ResStringPool_header),
pEntry->key.index,
keyStringPoolHeader.flags & ResStringPool_header::UTF8_FLAG
);
if(pEntry->flags & ResTable_entry::FLAG_COMPLEX) {
...
} else {
struct Res_value* pValue = (struct Res_value*)((unsigned char*)pEntry + sizeof(struct ResTable_entry));
printf("value :\n");
printValue(pValue, globalStringPoolHeader, pGlobalStrings);
printf("\n");
}
}
free(pOffsets);
free(pData);
}

pEntry->key.index就是资源的key在资源key字符串池中的序号了,直接打印即可。

然后找到struct ResTable_entry后面跟着的struct Res_value,这个结构体里面就是资源的值。但是这个值的获取比较复杂,我们先来看看这个结构体的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

/**
* Representation of a value in a resource, supplying type
* information.
*/
struct Res_value
{
// Number of bytes in this structure.
uint16_t size;

// Always set to 0.
uint8_t res0;

// Type of the data value.
enum {
// The 'data' is either 0 or 1, specifying this resource is either
// undefined or empty, respectively.
TYPE_NULL = 0x00,
// The 'data' holds a ResTable_ref, a reference to another resource
// table entry.
TYPE_REFERENCE = 0x01,
// The 'data' holds an attribute resource identifier.
TYPE_ATTRIBUTE = 0x02,
// The 'data' holds an index into the containing resource table's
// global value string pool.
TYPE_STRING = 0x03,
// The 'data' holds a single-precision floating point number.
TYPE_FLOAT = 0x04,
// The 'data' holds a complex number encoding a dimension value,
// such as "100in".
TYPE_DIMENSION = 0x05,
// The 'data' holds a complex number encoding a fraction of a
// container.
TYPE_FRACTION = 0x06,
// The 'data' holds a dynamic ResTable_ref, which needs to be
// resolved before it can be used like a TYPE_REFERENCE.
TYPE_DYNAMIC_REFERENCE = 0x07,
// The 'data' holds an attribute resource identifier, which needs to be resolved
// before it can be used like a TYPE_ATTRIBUTE.
TYPE_DYNAMIC_ATTRIBUTE = 0x08,

// Beginning of integer flavors...
TYPE_FIRST_INT = 0x10,

// The 'data' is a raw integer value of the form n..n.
TYPE_INT_DEC = 0x10,
// The 'data' is a raw integer value of the form 0xn..n.
TYPE_INT_HEX = 0x11,
// The 'data' is either 0 or 1, for input "false" or "true" respectively.
TYPE_INT_BOOLEAN = 0x12,

// Beginning of color integer flavors...
TYPE_FIRST_COLOR_INT = 0x1c,

// The 'data' is a raw integer value of the form #aarrggbb.
TYPE_INT_COLOR_ARGB8 = 0x1c,
// The 'data' is a raw integer value of the form #rrggbb.
TYPE_INT_COLOR_RGB8 = 0x1d,
// The 'data' is a raw integer value of the form #argb.
TYPE_INT_COLOR_ARGB4 = 0x1e,
// The 'data' is a raw integer value of the form #rgb.
TYPE_INT_COLOR_RGB4 = 0x1f,

// ...end of integer flavors.
TYPE_LAST_COLOR_INT = 0x1f,

// ...end of integer flavors.
TYPE_LAST_INT = 0x1f
};
uint8_t dataType;

// Structure of complex data values (TYPE_UNIT and TYPE_FRACTION)
enum {
// Where the unit type information is. This gives us 16 possible
// types, as defined below.
COMPLEX_UNIT_SHIFT = 0,
COMPLEX_UNIT_MASK = 0xf,

// TYPE_DIMENSION: Value is raw pixels.
COMPLEX_UNIT_PX = 0,
// TYPE_DIMENSION: Value is Device Independent Pixels.
COMPLEX_UNIT_DIP = 1,
// TYPE_DIMENSION: Value is a Scaled device independent Pixels.
COMPLEX_UNIT_SP = 2,
// TYPE_DIMENSION: Value is in points.
COMPLEX_UNIT_PT = 3,
// TYPE_DIMENSION: Value is in inches.
COMPLEX_UNIT_IN = 4,
// TYPE_DIMENSION: Value is in millimeters.
COMPLEX_UNIT_MM = 5,

// TYPE_FRACTION: A basic fraction of the overall size.
COMPLEX_UNIT_FRACTION = 0,
// TYPE_FRACTION: A fraction of the parent size.
COMPLEX_UNIT_FRACTION_PARENT = 1,

// Where the radix information is, telling where the decimal place
// appears in the mantissa. This give us 4 possible fixed point
// representations as defined below.
COMPLEX_RADIX_SHIFT = 4,
COMPLEX_RADIX_MASK = 0x3,

// The mantissa is an integral number -- i.e., 0xnnnnnn.0
COMPLEX_RADIX_23p0 = 0,
// The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn
COMPLEX_RADIX_16p7 = 1,
// The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn
COMPLEX_RADIX_8p15 = 2,
// The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn
COMPLEX_RADIX_0p23 = 3,

// Where the actual value is. This gives us 23 bits of
// precision. The top bit is the sign.
COMPLEX_MANTISSA_SHIFT = 8,
COMPLEX_MANTISSA_MASK = 0xffffff
};

// Possible data values for TYPE_NULL.
enum {
// The value is not defined.
DATA_NULL_UNDEFINED = 0,
// The value is explicitly defined as empty.
DATA_NULL_EMPTY = 1
};

// The data for this item, as interpreted according to dataType.
typedef uint32_t data_type;
data_type data;
};

我们先需要根据dataType判断这个值是什么类型的,然后再根据不同的类型,从data读取具体的值。读取的方法比较复杂,我就不具体讲解,大家可以参考我的demo代码理解。

我们找到bool部分的打印,可以看到key和value就都打印出来了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type: id=0x3,name=bool
80
0
0
config :
entryIndex: 0x0, key :
abc_action_bar_embed_tabs
value :
(boolean) true

entryIndex: 0x1, key :
abc_allow_stacked_button_bar
value :
(boolean) false

entryIndex: 0x2, key :
abc_config_actionMenuItemAllCaps
value :
(boolean) true

config : port
entryIndex: 0x0, key :
abc_action_bar_embed_tabs
value :
(boolean) false

ResTable_map_entry

从上面可以看出来ResTable_entry代表的是普通键值对的资源如string、bool、drawable等,那ResTable_map_entry又代表的是啥呢?

其实它代表的是类型style、attr的资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<attr name="buttonTintMode">
<enum name="src_over" value="3"/>
<enum name="src_in" value="5"/>
<enum name="src_atop" value="9"/>
<enum name="multiply" value="14"/>
<enum name="screen" value="15"/>
<enum name="add" value="16"/>
</attr>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

像上面的R.attr.buttonTintMode和R.style.AppTheme的值都需要用一个map去表示。

我们来看看struct ResTable_map_entry:

1
2
3
4
5
6
7
8
9
10
11
/**
* Extended form of a ResTable_entry for map entries, defining a parent map
* resource from which to inherit values.
*/
struct ResTable_map_entry : public ResTable_entry
{
// Resource identifier of the parent mapping, or 0 if there is none.
ResTable_ref parent;
// Number of name/value pairs that follow for FLAG_COMPLEX.
uint32_t count;
};

它的parent成员变量就定义了这个style的parent,count成员变量则代表了这个map的大小,也就是ResTable_map_entry后面跟着的键值对的数量。

资源的id

struct ResTable_ref也是一个需要重点讲解的结构体,它的定义很简单:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* This is a reference to a unique entry (a ResTable_entry structure)
* in a resource table. The value is structured as: 0xpptteeee,
* where pp is the package index, tt is the type index in that
* package, and eeee is the entry index in that type. The package
* and type values start at 1 for the first item, to help catch cases
* where they have not been supplied.
*/
struct ResTable_ref
{
uint32_t ident;
};

这个ident代表的就是资源的id。这个值其实我们在java里面也能看到:

1
2
3
4
5
6
7
8
9
public final class R {
...
public static final class bool {
public static final int abc_action_bar_embed_tabs=0x7f030000;
public static final int abc_allow_stacked_button_bar=0x7f030001;
public static final int abc_config_actionMenuItemAllCaps=0x7f030002;
}
...
}

资源的id其实是有固定的格式和含义的,它的格式如下:

0xpptteeee

头一个字节保存了packageId,接着的一个字节保存了typeId,后面的两个字节保存了entryIndex。例如我们的abc_allow_stacked_button_bar=0x7f030001,它的packageId=0x7f, typeId=0x3, entryIndex=0x1。

我们在解析package资源的时候就已经把package id打印了出来,它就是0x7f:

1
type:512, headSize:288, size:188068, id:7f, packageName:com.cvte.tv.myapplication

而在后面解析资源的时候也把typeId和entryIndex打印了出来:

1
2
3
4
5
6
7
8
9
10
11
12
type: id=0x3,name=bool
80
0
0
config :
entryIndex: 0x0, key :
abc_action_bar_embed_tabs
value :
(boolean) true

entryIndex: 0x1, key :
abc_allow_stacked_button_bar

于是乎我们就能定位到abc_allow_stacked_button_bar这个资源了。

所以我们的style的parent.ident就可以定位到style的parent资源。

有时候我们会看到packageId是0x01,在我们的resource.arsc里面找不到对应的package。这个package指定其实是系统资源包,我们在xml里面配置的@android:color/black就会使用到系统资源包里面的资源,这个资源是不会打包进我们的应用的:

1
2
3
4
5
6
7
8
9
10
11
12
...

type: id=0x4,name=color

...

entryIndex: 0x41, key :
primary_dark_material_dark
value :
(reference) 0x0106000c

...

ResTable_map

ResTable_map_entry后面跟着的键值对数组其实就是一个个的ResTable_map:

struct ResTable_map定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* A single name/value mapping that is part of a complex resource
* entry.
*/
struct ResTable_map
{
// The resource identifier defining this mapping's name. For attribute
// resources, 'name' can be one of the following special resource types
// to supply meta-data about the attribute; for all other resource types
// it must be an attribute resource.
ResTable_ref name;

// Special values for 'name' when defining attribute resources.
enum {
// This entry holds the attribute's type code.
ATTR_TYPE = Res_MAKEINTERNAL(0),

// For integral attributes, this is the minimum value it can hold.
ATTR_MIN = Res_MAKEINTERNAL(1),

// For integral attributes, this is the maximum value it can hold.
ATTR_MAX = Res_MAKEINTERNAL(2),

// Localization of this resource is can be encouraged or required with
// an aapt flag if this is set
ATTR_L10N = Res_MAKEINTERNAL(3),

// for plural support, see android.content.res.PluralRules#attrForQuantity(int)
ATTR_OTHER = Res_MAKEINTERNAL(4),
ATTR_ZERO = Res_MAKEINTERNAL(5),
ATTR_ONE = Res_MAKEINTERNAL(6),
ATTR_TWO = Res_MAKEINTERNAL(7),
ATTR_FEW = Res_MAKEINTERNAL(8),
ATTR_MANY = Res_MAKEINTERNAL(9)

};

// Bit mask of allowed types, for use with ATTR_TYPE.
enum {
// No type has been defined for this attribute, use generic
// type handling. The low 16 bits are for types that can be
// handled generically; the upper 16 require additional information
// in the bag so can not be handled generically for TYPE_ANY.
TYPE_ANY = 0x0000FFFF,

// Attribute holds a references to another resource.
TYPE_REFERENCE = 1<<0,

// Attribute holds a generic string.
TYPE_STRING = 1<<1,

// Attribute holds an integer value. ATTR_MIN and ATTR_MIN can
// optionally specify a constrained range of possible integer values.
TYPE_INTEGER = 1<<2,

// Attribute holds a boolean integer.
TYPE_BOOLEAN = 1<<3,

// Attribute holds a color value.
TYPE_COLOR = 1<<4,

// Attribute holds a floating point value.
TYPE_FLOAT = 1<<5,

// Attribute holds a dimension value, such as "20px".
TYPE_DIMENSION = 1<<6,

// Attribute holds a fraction value, such as "20%".
TYPE_FRACTION = 1<<7,

// Attribute holds an enumeration. The enumeration values are
// supplied as additional entries in the map.
TYPE_ENUM = 1<<16,

// Attribute holds a bitmaks of flags. The flag bit values are
// supplied as additional entries in the map.
TYPE_FLAGS = 1<<17
};

// Enum of localization modes, for use with ATTR_L10N.
enum {
L10N_NOT_REQUIRED = 0,
L10N_SUGGESTED = 1
};

// This mapping's value.
Res_value value;
};

它的name代表的就是这个键值对的key,而它的value代表的就是键值对的值。

name同样的是个struct ResTable_ref,它同样可以从资源id拿到对应的资源,但是这个name有点特殊,如果是它的ident的值是下面枚举中的一个的话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF))

enum {
// This entry holds the attribute's type code.
ATTR_TYPE = Res_MAKEINTERNAL(0),

// For integral attributes, this is the minimum value it can hold.
ATTR_MIN = Res_MAKEINTERNAL(1),

// For integral attributes, this is the maximum value it can hold.
ATTR_MAX = Res_MAKEINTERNAL(2),

// Localization of this resource is can be encouraged or required with
// an aapt flag if this is set
ATTR_L10N = Res_MAKEINTERNAL(3),

// for plural support, see android.content.res.PluralRules#attrForQuantity(int)
ATTR_OTHER = Res_MAKEINTERNAL(4),
ATTR_ZERO = Res_MAKEINTERNAL(5),
ATTR_ONE = Res_MAKEINTERNAL(6),
ATTR_TWO = Res_MAKEINTERNAL(7),
ATTR_FEW = Res_MAKEINTERNAL(8),
ATTR_MANY = Res_MAKEINTERNAL(9)
};

例如如果index==0x01000000,就代表name是ATTR_TYPE,也代表这个资源是attr。

此时,它的value也是特殊的,是下面枚举中的一个,代表attr的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
enum {
// No type has been defined for this attribute, use generic
// type handling. The low 16 bits are for types that can be
// handled generically; the upper 16 require additional information
// in the bag so can not be handled generically for TYPE_ANY.
TYPE_ANY = 0x0000FFFF,

// Attribute holds a references to another resource.
TYPE_REFERENCE = 1<<0,

// Attribute holds a generic string.
TYPE_STRING = 1<<1,

// Attribute holds an integer value. ATTR_MIN and ATTR_MIN can
// optionally specify a constrained range of possible integer values.
TYPE_INTEGER = 1<<2,

// Attribute holds a boolean integer.
TYPE_BOOLEAN = 1<<3,

// Attribute holds a color value.
TYPE_COLOR = 1<<4,

// Attribute holds a floating point value.
TYPE_FLOAT = 1<<5,

// Attribute holds a dimension value, such as "20px".
TYPE_DIMENSION = 1<<6,

// Attribute holds a fraction value, such as "20%".
TYPE_FRACTION = 1<<7,

// Attribute holds an enumeration. The enumeration values are
// supplied as additional entries in the map.
TYPE_ENUM = 1<<16,

// Attribute holds a bitmaks of flags. The flag bit values are
// supplied as additional entries in the map.
TYPE_FLAGS = 1<<17
};

解析代码如下:

1
2
3
4
5
6
7
if(pEntry->flags & ResTable_entry::FLAG_COMPLEX) {
struct ResTable_map_entry* pMapEntry = (struct ResTable_map_entry*)(pData + offset);
for(int i = 0; i <pMapEntry->count ; i++) {
struct ResTable_map* pMap = (struct ResTable_map*)(pData + offset + pMapEntry->size + i * sizeof(struct ResTable_map_entry));
printf("\tname:0x%x, valueType:%u, value:%u\n", pMap->name.ident, pMap->value.dataType, pMap->value.data);
}
}

让我们找到buttonTintMode的打印

1
2
3
4
5
6
7
8
9
entryIndex: 0x69, key :
buttonTintMode
name:0x1000000, valueType:16, value:65536
name:0x7f070019, valueType:16, value:16
name:0x7f070050, valueType:16, value:14
name:0x7f070061, valueType:16, value:15
name:0x7f070078, valueType:16, value:9
name:0x7f070079, valueType:16, value:5
name:0x7f07007a, valueType:16, value:3

第一个ResTable_ref的name的indent的值是0x1000000,就代表name是ATTR_TYPE,也代表这个资源是attr。然后value是65536,也就是TYPE_ENUM。

然后我们顺便找下7f070019、7f070050、7f070061、7f070078、7f070079、7f07007a资源的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
...

type: id=0x7,name=id

...

entryIndex: 0x19, key :
add
value :
(boolean) false

...

entryIndex: 0x50, key :
multiply
value :
(boolean) false

...

entryIndex: 0x61, key :
screen
value :
(boolean) false

...

entryIndex: 0x78, key :
src_atop
value :
(boolean) false

entryIndex: 0x79, key :
src_in
value :
(boolean) false

entryIndex: 0x7a, key :
src_over
value :
(boolean) false

Demo

完整的demo可以在github上找到:

https://github.com/bluesky466/ResourcesArscDemo

呼~长舒一口气,终于大功告成。