上篇博客寫到,Package資源剩下的部分是由多組RES_TABLE_TYPE_SPEC_TYPE和RES_TABLE_TYPE_TYPE構(gòu)成的赴背。
一個(gè)RES_TABLE_TYPE_SPEC_TYPE后面跟著一個(gè)或者多個(gè)RES_TABLE_TYPE_TYPE構(gòu)成一種類型的資源的描述(例如string類型哨免、bool類型、dimen類型等)
RES_TABLE_TYPE_SPEC_TYPE
我們接著來(lái)看看RES_TABLE_TYPE_SPEC_TYPE的頭部結(jié)構(gòu)體:
/**
* 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頭部后面會(huì)跟著entryCount個(gè)uint32_t,代表這種類型有entryCount個(gè)數(shù)據(jù),并且每個(gè)uint32_t標(biāo)識(shí)了這個(gè)數(shù)據(jù)在哪些configuration下有特殊的值假栓。
這些configuration可能是不同的地區(qū)、不同的屏幕分辨率芒炼、不同的sdk版本等:
// 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,
};
這里直接舉個(gè)例子,例如我們可能會(huì)在res/values目錄下創(chuàng)建一些bool配置:
<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目錄下我們會(huì)把a(bǔ)bc_action_bar_embed_tabs的值設(shè)置成false
<bool name="abc_action_bar_embed_tabs">false</bool>
然后下面代碼就能在橫屏批狐、豎屏下拿到不同的配置了:
context.getResources().getBoolean(R.bool.abc_action_bar_embed_tabs);
在代碼里面,我們可以先讀取ResTable_typeSpec,然后根據(jù)entryCount得到這種類型有多少個(gè)數(shù)據(jù)(例如這里的bool就有abc_action_bar_embed_tabs、abc_allow_stacked_button_bar鸳谜、abc_config_actionMenuItemAllCaps三個(gè)數(shù)據(jù),所以bool類型下的entryCount就是3),然后繼續(xù)讀entryCount個(gè)uint32_t,讀出來(lái)就是每個(gè)數(shù)據(jù)在哪些configuration下有特殊的值膝藕。
//printStringFromStringsPool:
void printStringFromStringsPool(uint32_t* pOffsets, char* pStringsStart, uint32_t stringIndex, uint32_t isUtf8) {
//前面兩個(gè)字節(jié)是長(zhǎng)度,要跳過(guò)
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類型下的打印:
...
type:id=3,name=bool
80
0
0
...
可以看到bool類型下的確有三個(gè)uint32_t,分別是80、0咐扭、0芭挽。這個(gè)80代表的就是CONFIG_ORIENTATION,也就是說(shuō)這個(gè)數(shù)據(jù)在不同的屏幕方向下面會(huì)有和默認(rèn)值不同的值滑废。而0則代表了這個(gè)數(shù)據(jù)只有一個(gè)默認(rèn)值,不會(huì)跟著configuration的變化而改變:
//configuration.h
ACONFIGURATION_ORIENTATION = 0x0080,
//ResTable_config里面的enum
CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,
讓我們返回去對(duì)比下:
// 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>
第一個(gè)abc_action_bar_embed_tabs在不同的屏幕方向下可能值會(huì)改變,所以它的uint32_t值是80,也就是CONFIG_ORIENTATION,而abc_allow_stacked_button_bar 和abc_config_actionMenuItemAllCaps只有默認(rèn)的配置,所以他們的uint32_t都是0。
所以RES_TABLE_TYPE_SPEC_TYPE的作用就是將數(shù)據(jù)受到哪些configuration影響都標(biāo)識(shí)出來(lái)袜爪。
在讀取數(shù)據(jù)的時(shí)候先看看它是否會(huì)受configuration影響,如果不會(huì),直接讀默認(rèn)的RES_TABLE_TYPE_TYPE里面的默認(rèn)值就好,否則就根據(jù)當(dāng)前的configuration去到后面對(duì)應(yīng)的RES_TABLE_TYPE_TYPE下面讀取對(duì)應(yīng)的值了蠕趁。
RES_TABLE_TYPE_TYPE
講的這里終于到了最重要的部分,我們?cè)趚ml里面配的值,都會(huì)在RES_TABLE_TYPE_TYPE里面體現(xiàn)出來(lái)。
我們照例先來(lái)看看它的頭部結(jié)構(gòu)體:
/**
* 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;
};
這個(gè)ResTable_type里有個(gè)config成員,它就是具體的配置了,我們可以把它打印出來(lái):
else if(RES_TABLE_TYPE_TYPE == type) {
fread((void*)&typeHeader, sizeof(struct ResTable_type), 1, pFile);
printConfig(typeHeader.config);
...
}
找到bool的那一段,可以看到它有兩個(gè)RES_TABLE_TYPE_TYPE,第一個(gè)是默認(rèn)的配置(values目錄),第二個(gè)是port下的配置(values-port目錄):
...
type: id=0x3,name=bool
80
0
0
config :
config : port
...
然后根據(jù)注釋的說(shuō)明我們知道,ResTable_type頭部后跟著entryCount個(gè)uint32_t,代表了每個(gè)entry相對(duì)entriesStart的偏移辛馆。這里和RES_STRING_POOL_TYPE有點(diǎn)像,也是從偏移數(shù)組讀取數(shù)據(jù)的偏移值,然后從entriesStart進(jìn)行偏移得到數(shù)據(jù)的地址俺陋。
那entriesStart后面的entry是什么呢?其實(shí)entry有兩種類型ResTable_entry和ResTable_map_entry昙篙。
他們其實(shí)是有繼承關(guān)系的,ResTable_map_entry是ResTable_entry的子類(這里的繼承關(guān)系是c++里面的繼承關(guān)系,前面我們都是用c語(yǔ)言去講的,但是這里必須引入c++了,不過(guò)也是最基礎(chǔ)的繼承而已,大家可以自行搜索下)腊状。
/**
* 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有個(gè)flags成員變量,如果它的FLAG_COMPLEX位被置1(也就是說(shuō)flags & 0x0001 != 0),則它是個(gè)ResTable_map_entry結(jié)構(gòu)。
兩種結(jié)構(gòu)的不同之處在于ResTable_entry后面跟著的是一個(gè)Res_value,而ResTable_map_entry后面跟著的是多個(gè)name/value鍵值對(duì),這個(gè)鍵值對(duì)是用struct ResTable_map來(lái)表示的苔可。
ResTable_entry
我們先從ResTable_entry講起,我們讀完struct ResTable_type頭部信息之后繼續(xù)將offset數(shù)組和entriesStart開始到剩下的部分都讀進(jìn)去保存到pOffset和pData中缴挖。
接著就可以用*(pOffsets + i)得到每個(gè)entry的偏移,再與entriesStart相加得到entry的具體位置。這里有一點(diǎn)需要注意的是如果offset是ResTable_type::NO_ENTRY,也就是0xFFFFFFFF的時(shí)候,代表它是無(wú)效的,直接跳過(guò)即可:
else if(RES_TABLE_TYPE_TYPE == type) {
fread((void*)&typeHeader, sizeof(struct ResTable_type), 1, pFile);
printConfig(typeHeader.config);
// 實(shí)際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字符串池中的序號(hào)了,直接打印即可焚辅。
然后找到struct ResTable_entry后面跟著的struct Res_value,這個(gè)結(jié)構(gòu)體里面就是資源的值映屋。但是這個(gè)值的獲取比較復(fù)雜,我們先來(lái)看看這個(gè)結(jié)構(gòu)體的定義:
/**
* 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;
};
我們先需要根據(jù)dataType判斷這個(gè)值是什么類型的,然后再根據(jù)不同的類型,從data讀取具體的值。讀取的方法比較復(fù)雜,我就不具體講解,大家可以參考我的demo代碼理解同蜻。
我們找到bool部分的打印,可以看到key和value就都打印出來(lái)了:
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
從上面可以看出來(lái)ResTable_entry代表的是普通鍵值對(duì)的資源如string棚点、bool、drawable等,那ResTable_map_entry又代表的是啥呢?
其實(shí)它代表的是類型style湾蔓、attr的資源:
<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的值都需要用一個(gè)map去表示乙濒。
我們來(lái)看看struct ResTable_map_entry:
/**
* 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成員變量就定義了這個(gè)style的parent,count成員變量則代表了這個(gè)map的大小,也就是ResTable_map_entry后面跟著的鍵值對(duì)的數(shù)量。
資源的id
struct ResTable_ref也是一個(gè)需要重點(diǎn)講解的結(jié)構(gòu)體,它的定義很簡(jiǎn)單:
/**
* 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;
};
這個(gè)ident代表的就是資源的id卵蛉。這個(gè)值其實(shí)我們?cè)趈ava里面也能看到:
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其實(shí)是有固定的格式和含義的,它的格式如下:
0xpptteeee
頭一個(gè)字節(jié)保存了packageId,接著的一個(gè)字節(jié)保存了typeId,后面的兩個(gè)字節(jié)保存了entryIndex颁股。例如我們的abc_allow_stacked_button_bar=0x7f030001,它的packageId=0x7f, typeId=0x3, entryIndex=0x1。
我們?cè)诮馕鰌ackage資源的時(shí)候就已經(jīng)把package id打印了出來(lái),它就是0x7f:
type:512, headSize:288, size:188068, id:7f, packageName:com.cvte.tv.myapplication
而在后面解析資源的時(shí)候也把typeId和entryIndex打印了出來(lái):
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這個(gè)資源了傻丝。
所以我們的style的parent.ident就可以定位到style的parent資源甘有。
有時(shí)候我們會(huì)看到packageId是0x01,在我們的resource.arsc里面找不到對(duì)應(yīng)的package。這個(gè)package指定其實(shí)是系統(tǒng)資源包,我們?cè)趚ml里面配置的@android:color/black就會(huì)使用到系統(tǒng)資源包里面的資源,這個(gè)資源是不會(huì)打包進(jìn)我們的應(yīng)用的:
...
type: id=0x4,name=color
...
entryIndex: 0x41, key :
primary_dark_material_dark
value :
(reference) 0x0106000c
...
ResTable_map
ResTable_map_entry后面跟著的鍵值對(duì)數(shù)組其實(shí)就是一個(gè)個(gè)的ResTable_map:
struct ResTable_map定義如下:
/**
* 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代表的就是這個(gè)鍵值對(duì)的key,而它的value代表的就是鍵值對(duì)的值葡缰。
name同樣的是個(gè)struct ResTable_ref,它同樣可以從資源id拿到對(duì)應(yīng)的資源,但是這個(gè)name有點(diǎn)特殊,如果是它的ident的值是下面枚舉中的一個(gè)的話:
#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,也代表這個(gè)資源是attr亏掀。
此時(shí),它的value也是特殊的,是下面枚舉中的一個(gè),代表attr的類型:
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
};
解析代碼如下:
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的打印
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
第一個(gè)ResTable_ref的name的indent的值是0x1000000,就代表name是ATTR_TYPE,也代表這個(gè)資源是attr。然后value是65536,也就是TYPE_ENUM泛释。
然后我們順便找下7f070019滤愕、7f070050、7f070061怜校、7f070078间影、7f070079、7f07007a資源的定義:
...
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
呼~長(zhǎng)舒一口氣,終于大功告成茄茁。