Apk中的resources.arsc是aapt工具編譯資源時(shí)生成的一個(gè)重要文件。App資源能根據(jù)配置的變化,索引到相應(yīng)的資源都要依賴它。例如Android設(shè)備語言,屏幕設(shè)備尺寸不同時(shí),app通過同樣的ID但卻能找到不同的資源進(jìn)行顯示。
資源打包過程簡(jiǎn)述
開發(fā)app時(shí),需要代碼和資源。最終生成的apk中代碼轉(zhuǎn)換為了dex文件,那么apk文件中的資源是否還是app開發(fā)時(shí)那些資源文件呢?或者說這些資源文件是否發(fā)生了什么變化?
引用老羅一張關(guān)于資源打包過程以及查找的圖:

從上圖可以看出:
除了assets和res/raw資源被原裝不動(dòng)地打包進(jìn)APK之外,其它的資源都會(huì)被編譯或者處理.xml文件會(huì)被編譯為二進(jìn)制的xml,所以解壓apk后,無法直接打開xml文件。
除了assets資源之外,其它的資源都會(huì)被賦予一個(gè)資源ID。
打包工具負(fù)責(zé)編譯和打包資源,編譯完成之后,會(huì)生成一個(gè)resources.arsc文件和一個(gè)R.java,前者保存的是一個(gè)資源索引表,后者定義了各個(gè)資源ID常量,供在代碼中索引資源。
應(yīng)用程序配置文件AndroidManifest.xml同樣會(huì)被編譯成二進(jìn)制的XML文件,然后再打包到APK里面去。
應(yīng)用程序在運(yùn)行時(shí)最終是通過AssetManager來訪問資源,或通過資源ID來訪問,或通過文件名來訪問。
在生成的apk中,只有assets和res/raw資源被原裝不動(dòng)地打包進(jìn)apk。其它的資源都會(huì)被編譯或者處理??梢允褂萌缦旅畈榭碼pk中的文件列表:
aapt l -v apkfile
將apk直接解壓后,會(huì)發(fā)現(xiàn)xml都打不開,提示格式不對(duì),因?yàn)槠湟呀?jīng)變?yōu)槎M(jìn)制xml了。另外PNG等圖片也會(huì)進(jìn)行相應(yīng)的優(yōu)化。還有就是多了一個(gè)resources.arsc文件。
需要準(zhǔn)備的東西
分析resources.arsc文件,肯定要現(xiàn)有它了。利用Android studio創(chuàng)建一個(gè)ResourceDemo的工程,
資源從取值上來分,可分為兩類:bag類型資源和非bag類型的資源。
bag資源:通俗的說,就是這類資源在賦值的時(shí)候,不能隨便賦值,只能從事先定義好的值中選取一個(gè)賦值。很像枚舉。
類型為values的資源除了是string之外,還有其它很多類型的資源,其中有一些比較特殊,如bag、style、plurals和array類的資源。這些資源會(huì)給自己定義一些專用的值,這些帶有專用值的資源就統(tǒng)稱為Bag資源。
例如,Android系統(tǒng)提供的android:orientation屬性的取值范圍為{“vertical”、“horizontal”},就相當(dāng)于是定義了vertical和horizontal兩個(gè)Bag。
在res/values中創(chuàng)建attrs.xml文件,在其中自定一個(gè)bag類型的屬性資源。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="custom_orientation">
<enum name="custom_vertical" value="100" />
<enum name="custom_horizontal" value="200" />
</attr>
</resources>
這個(gè)文件定義了一個(gè)名稱為“custom_orientation”的屬性資源,它是一個(gè)枚舉格式(也可理解為枚舉類型)的屬性,可以取值為“custom_vertical”或者“custom_horizontal”。
custom_vertical和custom_horizontal是custom_orientation的兩個(gè)bag,我們可以將custom_vertical和custom_horizontal看成是custom_orientation的兩個(gè)元數(shù)據(jù),用來描述custom_orientation的取值集合。
“custom_orientation”是一個(gè)枚舉類型的attr屬性資源,也要使用一個(gè)內(nèi)部元數(shù)據(jù)來描述其屬性類型,這個(gè)元數(shù)據(jù)也使用一個(gè)bag來表示。
也就是說custom_orientation是由三個(gè)bag構(gòu)成的:
第一個(gè)bag:名稱是“^type”,值是TYPE_ENUM(TYPE_ENUM = 1<<16)
第二個(gè)bag:名稱是“custom_vertical”,值是100
第三個(gè)bag: 名稱是“custom_horizontal”,值是200
另外還要給這個(gè)bag分配資源ID,因?yàn)檫@些枚舉值是通過名稱,例如custom_vertical被引用使用的,所以也要給其分配資源ID,
資源ID的格式是PPTTEEEE,其中TT代表資源類型。那么:
名稱是“^type”的bag其分配的資源ID是attr類型的,而“custom_vertical”和“custom_horizontal”被分配到的資源ID是id類型的,所以在代碼中可以通過下面的形式引用其值:
R.attr.custom_orientation;
R.id.custom_horizontal;
R.id.custom_vertical;
非bag資源:通俗的說,就是這類資源賦值的時(shí)候,很隨意,可以任意指定。
以res/values/strings.xml為例:
<resources>
<string name="app_name">ResourceDemo</string>
</resources>
該文件中定義了一個(gè)名字為“app_name”的string類型的資源,資源值為ResourceDemo。
將這個(gè)ResourceDemo工程編譯之后,解壓APK,就可以得到resources.arsc文件了。
要做什么
接下來就是分析resources.arsc,看看前面我們指出的那三個(gè)bag資源和一個(gè)非bag資源是以什么樣的形式存儲(chǔ)在resources.arsc的什么位置的。
只要搞清楚了這個(gè),那么就沉底搞清楚resources.arsc文件的格式了。
resources.arsc
resources.arsc文件的作用就是通過一樣的ID,根據(jù)不同的配置索引到最佳的資源顯示在UI中。
從整體上來看,其結(jié)構(gòu)為:資源索引表頭部+字符串資源池+N個(gè)Package數(shù)據(jù)塊。文件格式:

這張神圖在網(wǎng)上廣為流傳,但是其下半部分很容易讓人產(chǎn)生誤解,因?yàn)閷?shí)際上Type Spec和 Config List是交替出現(xiàn)的,而且一個(gè)Type Spec通常有不止一個(gè)config list.
不想在畫圖了,所以仍舊以此神圖為模板分析resources.arsc文件吧。
這里不去糾結(jié)這個(gè)文件是如何生成的,咱們逆其道而行,從文件本身窺探它是有什么組成的。
resources.arsc文件的結(jié)構(gòu)分割符
從上面所示的神圖中可以看到arsc文件是由若干種chunk組成的,而每一種chunk都是由一個(gè)頭部來記錄一些相關(guān)信息,例如該部分是什么,占多大空間等。
而每種chunk的頭部又是在一個(gè)基礎(chǔ)頭部上擴(kuò)展而來的的,這個(gè)基礎(chǔ)頭部是strcut Resheader:
源碼路徑:
AOSP-6.0/frameworks/base/include/androidfw/ResourceTypes.h
struct Resheader
{
//表示這是一個(gè)什么chunk
uint16_t type;
//chunk header 大小
uint16_t headerSize;
// chunk headr + chunk data,也就是 chunk的總大小
uint32_t size;
};
每一部分的頭部也是一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體繼承自Resheader(按照C語言來理解,就是結(jié)構(gòu)體的首元素是Resheader)。
在resources.arsc中type的取值有:
RES_NULL_TYPE = 0x0000,
RES_STRING_POOL_TYPE = 0x0001,
RES_TABLE_TYPE = 0x0002,
RES_XML_TYPE = 0x0003,
// Chunk types in RES_TABLE_TYPE
RES_TABLE_PACKAGE_TYPE = 0x0200,
RES_TABLE_TYPE_TYPE = 0x0201,
RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
RES_TABLE_LIBRARY_TYPE = 0x0203
resources.arsc頭部
resources.arsc頭部,即索引表頭部,其結(jié)構(gòu)如下:
struct ResTable_header
{
struct Resheader header;
// 該resources.arsc文件中包含幾個(gè)package資源包,
// 通常一個(gè)app只會(huì)包一個(gè)package資源包,就是自己
uint32_t packageCount;
};
這里header.headerSize就是這個(gè)struct ResTable_header的大小,header.size是這個(gè)resources.arsc文件的大小。而pacakageCount為1.
resources.arsc的文件是一個(gè)索引表,是RES_TABLE_TYPE,也就是說header.type為RES_TABLE_TYPE。
代碼驗(yàn)證:
struct stat buf;
stat("./resources.arsc", &buf);
int fd = open("./resources.arsc",0644);
uint8_t *data = (uint8_t*)mmap(NULL,buf.st_size,PROT_READ,MAP_PRIVATE,fd,0);
printf("################# res 文件頭部信息 #################\n");
ResTable_header *resHd = (ResTable_header*)data;
printf("res type = %p\n",resHd->header.type);
printf("res chunk hd size = %p\n",resHd->header.headerSize);
printf("res chunk size = %p\n",resHd->header.size);
printf("res packages count = %p\n",resHd->packageCount);
結(jié)果:
################# res 文件頭部信息 #################
res type = 0x2
res chunk hd size = 0xc
res chunk size = 0x2f268
res packages count = 0x1
0x2與RES_TABLE_TYPE相等,packages count為1都與預(yù)期相一致。
字符串資源值池
這一部分的存儲(chǔ)的字符串,都是資源的值,而且值是字符串類型。
以res/values/strings.xml為例:
<resources>
<string name="app_name">ResourceDemo</string>
</resources>
該文件中定義了一個(gè)名字為“app_name”的string類型的資源,資源值為ResourceDemo。
ResourceDemo就存在這一部分,而"app_name"與"string"并沒有存儲(chǔ)在這里。
這一字符串池也包含一個(gè)頭部:
struct ResStringPool_header
{
struct Resheader header;
// 字符串個(gè)數(shù)
uint32_t stringCount;
//字符串樣式個(gè)數(shù)
uint32_t styleCount;
// Flags
enum {
// If set, the string index is sorted by the string values (based
// on strcmp16()).
SORTED_FLAG = 1<<0,
// String pool is encoded in UTF-8
UTF8_FLAG = 1<<8
};
// 該字符串是string16還是string8類型
uint32_t flags;
//字符數(shù)組相對(duì)頭部的位置
uint32_t stringsStart;
//樣式數(shù)組相對(duì)頭部的位置
uint32_t stylesStart;
}
簡(jiǎn)單介紹這個(gè)字符串池如何存儲(chǔ)和索引字符串。
從神圖中可以看出緊跟著頭部的后面是兩個(gè)uint32類型的數(shù)組:字符串偏移數(shù)組和樣式偏移數(shù)組,數(shù)組元素個(gè)數(shù)分別為stringCount和styleCount.
這兩個(gè)數(shù)組后面之后還有兩個(gè)字符數(shù)組:字符串字符數(shù)組和與樣式字符數(shù)組。這兩個(gè)字符數(shù)組都很大很大。。。。
字符串偏移數(shù)組中的元素,就是一個(gè)字符串在字符串字符數(shù)組中的索引,而且根據(jù)索引得到的字符串的前面兩個(gè)字節(jié)表示其長(zhǎng)度,而且是以NULL結(jié)尾的,所以不會(huì)索引到其他內(nèi)容。
樣式偏移數(shù)組中的元素,就是一個(gè)樣式在樣式字符數(shù)組中的索引。這里不考慮樣式的情況。有興趣的可以參考老羅的博客。
現(xiàn)在已經(jīng)搞清楚這個(gè)字符串池的結(jié)構(gòu)了,而且也知道"ResourceDemo"這個(gè)字符串應(yīng)該就這個(gè)字符串池里,只不過在這個(gè)字符串前面加了兩個(gè)字節(jié),表示其長(zhǎng)度。
這兩個(gè)用于存儲(chǔ)長(zhǎng)度的字節(jié),并不是單純的把長(zhǎng)度存儲(chǔ)在這兩個(gè)字節(jié)中,而是有規(guī)則的:
與字符串格式是string8還是string16相關(guān)
string8類型的長(zhǎng)度解碼如下:
static inline size_t
decodeLength(const uint8_t** str)
{
size_t len = **str;
if ((len & 0x80) != 0) {
(*str)++;
len = ((len & 0x7F) << 8) | **str;
}
(*str)++;
return len;
}
傳入的參數(shù)是從字符串字符數(shù)組中以字符串偏移數(shù)組中的偏移為索引的字符串(前兩個(gè)字節(jié)是長(zhǎng)度)。
- string16類型長(zhǎng)度解碼如下:
static inline size_t
decodeLength(const uint16_t** str)
{
size_t len = **str;
if ((len & 0x8000) != 0) {
(*str)++;
len = ((len & 0x7FFF) << 16) | **str;
}
(*str)++;
return len;
}
那么來驗(yàn)證一下:
printf("################# resStringPool(資源項(xiàng)的值字符串資源池)頭部信息 #################\n");
ResStringPool_header *resStrPoolHd = (ResStringPool_header *)((uint8_t*)resHd+resHd->header.headerSize);
printf("type = %p\n",resStrPoolHd->header.type);
// 這個(gè)chunk數(shù)據(jù)塊頭部的大小
printf("chunk hd size = %p\n",resStrPoolHd->header.headerSize);
// 這個(gè)chunk數(shù)據(jù)塊的大小
printf("chunk size = %p\n",resStrPoolHd->header.size);
printf("stringCount = %p\n",resStrPoolHd->stringCount);
printf("styleCount = %p\n",resStrPoolHd->styleCount);
printf("flags = %p\n",resStrPoolHd->flags);
printf("stringsStart = %p\n",resStrPoolHd->stringsStart);
printf("stylesStart = %p\n",resStrPoolHd->stylesStart);
// header后面緊接著是兩個(gè)偏移數(shù)組,之后才是數(shù)據(jù)
// 字符串偏移數(shù)組,數(shù)組元素個(gè)數(shù)是stringCount
const uint32_t* mEntries = (const uint32_t*)(data+sizeof(ResTable_header)+resStrPoolHd->header.headerSize);
const uint32_t* mEntryStyles;
uint32_t mStylePoolSize;
// 字符串池地址
const void * mString = (const void *)((const uint8_t*)resStrPoolHd+resStrPoolHd->stringsStart);
const void * mStyleString;
if(resStrPoolHd->styleCount>0){
// 字符串樣式偏移數(shù)組,數(shù)組個(gè)數(shù)是styleCount,樣式數(shù)組和字符串?dāng)?shù)組一一對(duì)應(yīng)
// 也就是說在字符串偏移數(shù)組中所以為N的字符串,其樣式在樣式數(shù)組中的索引也為N
mEntryStyles = mEntries + resStrPoolHd->stringCount;
mStyleString = (void *)((const uint8_t*)resStrPoolHd+resStrPoolHd->stylesStart);
mStylePoolSize = (resStrPoolHd->header.size-resStrPoolHd->stylesStart)/sizeof(uint32_t);
}
for(int i=0;i<resStrPoolHd->stringCount;i++){
// 加2是跳過長(zhǎng)度
if(strcmp(((char*)mString)+mEntries[i]+2,"ResourceDemo")==0){
const uint8_t * str = (((uint8_t*)mString)+mEntries[i]);
// 字符串長(zhǎng)度
int len = decodeLength(&str);
printf("-->len = %p\n",len);
printf("-->idx = %p\n",i);
// 前兩字節(jié)是長(zhǎng)度
printf("-->%s\n",(((uint8_t*)mString)+mEntries[i])+2);
}
}
測(cè)試結(jié)果:
################# resStringPool(資源項(xiàng)的值字符串資源池)頭部信息 #################
type = 0x1
chunk hd size = 0x1c
chunk size = 0xcfb8
stringCount = 0x610
styleCount = (nil)
flags = 0x100
stringsStart = 0x185c
stylesStart = (nil)
-->len = 0xc
-->idx = 0x148
-->ResourceDemo
type為RES_STRING_POOL_TYPE正確。
flags為0x100,表明是string8,也就是utf-8字符串。
len為0xc,即12,而ResourceDemo長(zhǎng)度為12,也正確。也找到了ResourceDemo。
package數(shù)據(jù)部分
這一部分最為復(fù)雜。索引表頭部中的packageCount記錄了索引表中有多少各package數(shù)據(jù)部分。通常只有一個(gè)。
同樣這一部分的開頭也是一個(gè)頭部,結(jié)構(gòu)如下:
struct ResTable_package
{
struct Resheader header;
// 包ID
uint32_t id;
//package名字,string16形式存儲(chǔ)
uint16_t name[128];
// 類型字符串池,相對(duì)package頭部的偏移
uint32_t typeStrings;
// 包中共有資源類型的種數(shù)
uint32_t lastPublicType;
// 資源項(xiàng)名稱字符串池,相對(duì)package頭部的偏移
uint32_t keyStrings;
// 資源項(xiàng)的數(shù)量
uint32_t lastPublicKey;
uint32_t typeIdOffset;
};
其中ID是由命名規(guī)則的,系統(tǒng)資源包id為0x1,而app的資源包ID為0x7f,0x1-0x7f的都是合法的。
從神圖中可以看到緊跟著這個(gè)頭部的是兩個(gè)字符串池,都和前面介紹的資源項(xiàng)字符串池結(jié)構(gòu)一樣。
那么這兩個(gè)字符串池用來存儲(chǔ)什么東東呢?
仍以res/values/strings.xml為例:
<resources>
<string name="app_name">ResourceDemo</string>
</resources>
該文件中定義了一個(gè)名字為“app_name”的string類型的資源項(xiàng),資源值為ResourceDemo。
ResourceDemo就存在在前面介紹的資源項(xiàng)字符串池中,"string"存儲(chǔ)在類型字符串池中,“app_name”存儲(chǔ)在資源項(xiàng)名稱字符串池中。
代碼驗(yàn)證下:
printf("################# ResTablePackage頭部信息 #################\n");
ResTablePackage_header *resTablePackageHd = (ResTablePackage_header *)(data+sizeof(ResTable_header)+resStrPoolHd->header.size);
printf("resTablePackage type = %p\n",resTablePackageHd->header.type);
printf("resTablePackage chunk hd size = %p\n",resTablePackageHd->header.headerSize);
printf("resTablePackage chunk size = %p\n",resTablePackageHd->header.size);
printf("resTablePackage id = %p\n",resTablePackageHd->id);
char *name = allocFromUTF16(resTablePackageHd->name,128);
printf("resTablePackage name = %s\n",name);
free(name);
name = NULL;
printf("resTablePackage typeStrings = %p\n",resTablePackageHd->typeStrings);
//目前這個(gè)值設(shè)置為類型字符串資源池的元素個(gè)數(shù)
printf("resTablePackage lastPublicType = %p\n",resTablePackageHd->lastPublicType);
printf("resTablePackage keyStrings = %p\n",resTablePackageHd->keyStrings);
//目前這個(gè)值設(shè)置為資源項(xiàng)名稱字符串資源池的元素個(gè)數(shù)
printf("resTablePackage lastPublicKey = %p\n",resTablePackageHd->lastPublicKey);
printf("resTablePackage typeIdOffset = %p\n",resTablePackageHd->typeIdOffset);
printf("################# Type String pool信息 #################\n");
ResStringPool_header *typeStringPoolHd = (ResStringPool_header *)((uint8_t*)resTablePackageHd+resTablePackageHd->header.headerSize);
printf("typeStringPoolHd type = %p\n",typeStringPoolHd->header.type);
printf("typeStringPoolHd chunk hd size = %p\n",typeStringPoolHd->header.headerSize);
printf("typeStringPoolHd chunk size = %p\n",typeStringPoolHd->header.size);
printf("stringCount = %p\n",typeStringPoolHd->stringCount);
printf("styleCount = %p\n",typeStringPoolHd->styleCount);
printf("flags = %p\n",typeStringPoolHd->flags);
printf("stringsStart = %p\n",typeStringPoolHd->stringsStart);
printf("stylesStart = %p\n",typeStringPoolHd->stylesStart);
// header后面緊接著是兩個(gè)偏移數(shù)組,之后才是數(shù)據(jù)
// 字符串偏移數(shù)組,數(shù)組元素個(gè)數(shù)是stringCount
const uint32_t* mTypeEntries = (const uint32_t*)((uint8_t*)typeStringPoolHd+typeStringPoolHd->header.headerSize);
const uint32_t* mTypeEntryStyles;
uint32_t mTypeStylePoolSize;
// 字符串池地址
const void * mTypeString = (const void *)((const uint8_t*)typeStringPoolHd+typeStringPoolHd->stringsStart);
const void * mTypeStyleString;
if(typeStringPoolHd->styleCount>0){
// 字符串樣式偏移數(shù)組,數(shù)組個(gè)數(shù)是styleCount,樣式數(shù)組和字符串?dāng)?shù)組一一對(duì)應(yīng)
// 也就是說在字符串偏移數(shù)組中所以為N的字符串,其樣式在樣式數(shù)組中的索引也為N
mTypeEntryStyles = mTypeEntries + typeStringPoolHd->stringCount;
mTypeStyleString = (void *)((const uint8_t*)typeStringPoolHd+typeStringPoolHd->stylesStart);
mTypeStylePoolSize = (typeStringPoolHd->header.size-typeStringPoolHd->stylesStart)/sizeof(uint32_t);
}
printf("---->res type: \n");
for(int i=0;i<typeStringPoolHd->stringCount;i++){
const uint8_t * str = (((uint8_t*)mTypeString)+mTypeEntries[i]);
int len = decodeLength(&str);
//printf("-->len = %p\n",decodeLength(&str));
//printf("-->idx = %p\n",i);
printf("-->%s\n",(((uint8_t*)mTypeString)+mTypeEntries[i])+2);
}
printf("################# key String pool信息 #################\n");
ResStringPool_header *keyStringPoolHd = (ResStringPool_header *)((uint8_t*)typeStringPoolHd+typeStringPoolHd->header.size);
printf("keyStringPoolHd type = %p\n",keyStringPoolHd->header.type);
printf("keyStringPoolHd chunk hd size = %p\n",keyStringPoolHd->header.headerSize);
printf("keyStringPoolHd chunk size = %p\n",keyStringPoolHd->header.size);
printf("stringCount = %p\n",keyStringPoolHd->stringCount);
printf("styleCount = %p\n",keyStringPoolHd->styleCount);
printf("flags = %p\n",keyStringPoolHd->flags);
printf("stringsStart = %p\n",keyStringPoolHd->stringsStart);
printf("stylesStart = %p\n",keyStringPoolHd->stylesStart);
// header后面緊接著是兩個(gè)偏移數(shù)組,之后才是數(shù)據(jù)
// 字符串偏移數(shù)組,數(shù)組元素個(gè)數(shù)是stringCount
const uint32_t* mKeyEntries = (const uint32_t*)((uint8_t*)keyStringPoolHd+keyStringPoolHd->header.headerSize);
const uint32_t* mKeyEntryStyles;
uint32_t mKeyStylePoolSize;
// 字符串池地址
const void * mKeyString = (const void *)((const uint8_t*)keyStringPoolHd+keyStringPoolHd->stringsStart);
const void * mKeyStyleString;
if(keyStringPoolHd->styleCount>0){
// 字符串樣式偏移數(shù)組,數(shù)組個(gè)數(shù)是styleCount,樣式數(shù)組和字符串?dāng)?shù)組一一對(duì)應(yīng)
// 也就是說在字符串偏移數(shù)組中所以為N的字符串,其樣式在樣式數(shù)組中的索引也為N
mKeyEntryStyles = mKeyEntries + keyStringPoolHd->stringCount;
mKeyStyleString = (void *)((const uint8_t*)keyStringPoolHd+keyStringPoolHd->stylesStart);
mKeyStylePoolSize = (keyStringPoolHd->header.size-keyStringPoolHd->stylesStart)/sizeof(uint32_t);
}
printf("---->res key: \n");
for(int i=0;i<keyStringPoolHd->stringCount;i++){
if(strcmp(((char*)mKeyString)+mKeyEntries[i]+2,"app_name")==0){
const uint8_t * str = (((uint8_t*)mKeyString)+mKeyEntries[i]);
int len = decodeLength(&str);
printf("-->%s\n",(((uint8_t*)mKeyString)+mKeyEntries[i])+2);
}
}
結(jié)果:
################# ResTablePackage頭部信息 #################
resTablePackage type = 0x200
resTablePackage chunk hd size = 0x120
resTablePackage chunk size = 0x222a4
resTablePackage id = 0x7f
resTablePackage name = com.godin.resourcedemo
resTablePackage typeStrings = 0x120
resTablePackage lastPublicType = 0xc
resTablePackage keyStrings = 0x1d0
resTablePackage lastPublicKey = 0x3a8
resTablePackage typeIdOffset = (nil)
################# Type String pool信息 #################
typeStringPoolHd type = 0x1
typeStringPoolHd chunk hd size = 0x1c
typeStringPoolHd chunk size = 0xb0
stringCount = 0xc
styleCount = (nil)
flags = 0x100
stringsStart = 0x4c
stylesStart = (nil)
---->res type:
-->attr
-->drawable
-->mipmap
-->layout
-->anim
-->string
-->bool
-->dimen
-->style
-->integer
-->color
-->id
################# key String pool信息 #################
keyStringPoolHd type = 0x1
keyStringPoolHd chunk hd size = 0x1c
keyStringPoolHd chunk size = 0x7f38
stringCount = 0x3a8
styleCount = (nil)
flags = 0x100
stringsStart = 0xebc
stylesStart = (nil)
---->res key:
-->app_name
resTablePackage type 是RES_TABLE_PACKAGE_TYPE 正確。
也打印出該資源包中的所有資源類型和找到了"app_name"這個(gè)資源項(xiàng)。
Type Spec與Config List
神圖中的Type Spec 和 Config List 仍然歸屬在package數(shù)據(jù)部分.
這塊內(nèi)容是資源索引表中最重要的部分,但也是神圖沒能表達(dá)清楚的地方。這一部分也是同一個(gè)資源ID在不同配置下,找到不同資源文件的關(guān)鍵。所以這里先對(duì)這部分結(jié)構(gòu)進(jìn)行補(bǔ)充。
該部分的整體結(jié)構(gòu)以資源類型Type分段,每段的數(shù)據(jù)結(jié)構(gòu)相似,都是以ResTable_typeSpec開頭,后面緊跟著一個(gè)spec數(shù)組,若干ResTable_type,每個(gè)ResTable_type之后緊跟著ResTable_entry偏移數(shù)組和若干ResTable_entry。
要注意:ResTable_typeSpec中的chunk header的size包括了其后面緊跟的spec數(shù)組所占空間大小,ResTable_type中的chunk header的size同樣包括了跟隨在其后面的數(shù)據(jù)大小。
然后又以一個(gè)資源type的ResTable_typeSpec開頭,后面還跟這上面說的那些結(jié)構(gòu)。直到所有的資源Type都存放完畢。
也就是說一個(gè)resources.arsc中的資源type有多少,就會(huì)有多少個(gè)。ResTable_typeSpec結(jié)構(gòu)。
Type Spec數(shù)結(jié)構(gòu)定義如下:
struct ResTable_typeSpec
{
struct Resheader header;
// 類型ID
uint8_t id;
// Must be 0.
uint8_t res0;
// Must be 0.
uint16_t res1;
// 該類型資源項(xiàng)的數(shù)量
uint32_t entryCount;
enum {
// Additional flag indicating an entry is public.
SPEC_PUBLIC = 0x40000000
};
};
緊跟著這個(gè)結(jié)構(gòu)后面的是一個(gè)uint32_t類型的數(shù)組,該數(shù)組元素?cái)?shù)量為entryCount。
數(shù)組中的uint32_t數(shù)據(jù)位圖表示資源的配置。而且如果這個(gè)資源是可以導(dǎo)出的資源,那么其SPEC_PUBLICbit位置1.其余的配置的bit位如下:
ACONFIGURATION_MCC = 0x0001,
ACONFIGURATION_MNC = 0x0002,
ACONFIGURATION_LOCALE = 0x0004,
ACONFIGURATION_TOUCHSCREEN = 0x0008,
ACONFIGURATION_KEYBOARD = 0x0010,
ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
ACONFIGURATION_NAVIGATION = 0x0040,
ACONFIGURATION_ORIENTATION = 0x0080,
ACONFIGURATION_DENSITY = 0x0100,
ACONFIGURATION_SCREEN_SIZE = 0x0200,
ACONFIGURATION_VERSION = 0x0400,
ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
ACONFIGURATION_UI_MODE = 0x1000,
ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000,
ACONFIGURATION_LAYOUTDIR = 0x4000,
ACONFIGURATION_SCREEN_ROUND = 0x8000,
enum {
CONFIG_MCC = ACONFIGURATION_MCC,
CONFIG_MNC = ACONFIGURATION_MNC,
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,
CONFIG_SCREEN_ROUND = ACONFIGURATION_SCREEN_ROUND,
};
以mipmap類型的資源(存儲(chǔ)app icon)為例:
├── mipmap-hdpi-v4
│ └── ic_launcher.png
├── mipmap-mdpi-v4
│ └── ic_launcher.png
├── mipmap-xhdpi-v4
│ └── ic_launcher.png
├── mipmap-xxhdpi-v4
│ └── ic_launcher.png
└── mipmap-xxxhdpi-v4
└── ic_launcher.png
mipmap類型的資源,提供了五種配置,以適應(yīng)mdpi,hdpi,xhdpi,xxhdpi,xxxhpdi等不同屏幕尺寸。
mipmap類型的資源項(xiàng)名稱為ic_launcher(注意不包括文件的后綴),對(duì)應(yīng)著五個(gè)文件,app運(yùn)行時(shí),會(huì)根據(jù)當(dāng)時(shí)的系統(tǒng)配置選擇最佳的文件來顯示。
以此為例的話,entryCount為1.配置數(shù)組spec元素?cái)?shù)量也是為1了。
每種資源類型,在resources.arsc中只會(huì)存在一個(gè)ResTable_typeSpec數(shù)據(jù)結(jié)構(gòu),用來規(guī)范這個(gè)資源類型,比如這個(gè)資源類型中的資源項(xiàng)是否有配置(即可選資源),有哪些配置等。
緊跟在配置數(shù)組后面的是Config list,其對(duì)于的數(shù)據(jù)結(jié)構(gòu)是:
struct ResTable_type
{
struct Resheader header;
enum {
NO_ENTRY = 0xFFFFFFFF
};
// 類型ID
uint8_t id;
// Must be 0.
uint8_t res0;
// Must be 0.
uint16_t res1;
// 該類型資源項(xiàng)的數(shù)量
uint32_t entryCount;
// 該資源項(xiàng)值在 ResTable_entry數(shù)據(jù)部分的中的偏移
uint32_t entriesStart;
// 該資源的配置.
ResTable_config config;
};
ResTable_type這個(gè)數(shù)據(jù)結(jié)構(gòu)的數(shù)量,與配置的種類有關(guān),比如這個(gè)例子中的ic_launcher有五中配置,那么就會(huì)存在五個(gè)這樣的數(shù)據(jù)結(jié)構(gòu)。
假設(shè)res/mipmap-hdpi-v4中除了ic_launcher.png外還有一個(gè)名為test.png,而其他配置的mipmap文件夾中沒有這個(gè)文件,那么也還是只有五中配置,也就是五個(gè)ResTable_type數(shù)據(jù)結(jié)構(gòu)。
也就是說ResTable_type數(shù)據(jù)結(jié)構(gòu)的數(shù)量,由某一種類型資源中配置最多的資源項(xiàng)來決定,其數(shù)量等于該資源項(xiàng)的配置數(shù)量。
接下來希望在代碼中驗(yàn)證mipmap類型的entryCount是1,有五個(gè)ResTable_type數(shù)據(jù)結(jié)構(gòu)。
printf("#################### type spec信息 ####################\n");
ResTable_typeSpec *resTableTypeSpecHd = NULL;
ResTable_type *resTableTypeHd = NULL;
// 得到第一個(gè)type的ResTable_typeSpec結(jié)構(gòu)
Resheader *chunk_hd = (Resheader *)((uint8_t*)keyStringPoolHd+keyStringPoolHd->header.size);
int num = 0;
// 資源包中有幾個(gè)資源類型,就有幾個(gè)ResTable_typeSpec結(jié)構(gòu)
for(int i=0;i<resTablePackageHd->lastPublicType;i++){
// ResTable_typeSpec后面有若干ResTable_type,
// 通過 chunk header的type來區(qū)分
// 說明是TYPE_TYPE
while(chunk_hd->type == RES_TABLE_TYPE_TYPE){
resTableTypeHd = (ResTable_type *)chunk_hd;
// 統(tǒng)計(jì)mipmap類型的的ResTable_type結(jié)構(gòu)有多少
if(strcmp((((uint8_t*)mTypeString)+mTypeEntries[resTableTypeHd->id-1]+2),"mipmap")==0){
num +=1;
}
chunk_hd = (Resheader *)((uint8_t*)chunk_hd+chunk_hd->size);
}
//這是一個(gè)新的ResTable_typeSpec
resTableTypeSpecHd = (ResTable_typeSpec*)chunk_hd;
printf("resTableTypeSpecHd type = %p\n",resTableTypeSpecHd->header.type);
printf("resTableTypeSpecHd chunk hd size = %p\n",resTableTypeSpecHd->header.headerSize);
printf("resTableTypeSpecHd chunk size = %p\n",resTableTypeSpecHd->header.size);
printf("resTableTypeSpecHd id = %p\n",resTableTypeSpecHd->id);
// 頭部后面緊跟著數(shù)組元素個(gè)數(shù)為entryCount的uint32_t數(shù)組,每一個(gè)數(shù)組元素都用來描述一個(gè)資源項(xiàng)的配置差異性的。
printf("resTableTypeSpecHd entryCount = %p\n",resTableTypeSpecHd->entryCount);
chunk_hd = (Resheader *)((uint8_t*)chunk_hd+chunk_hd->size);
}
printf("-mipmap ResTable_type count is:%d\n",num);
結(jié)果:
#################### type spec信息 ####################
resTableTypeSpecHd type = 0x202
resTableTypeSpecHd chunk hd size = 0x10
resTableTypeSpecHd chunk size = 0x368
resTableTypeSpecHd id = 0x1
resTableTypeSpecHd entryCount = 0xd6
resTableTypeSpecHd type = 0x202
resTableTypeSpecHd chunk hd size = 0x10
resTableTypeSpecHd chunk size = 0x144
resTableTypeSpecHd id = 0x2
resTableTypeSpecHd entryCount = 0x4d
resTableTypeSpecHd type = 0x202
resTableTypeSpecHd chunk hd size = 0x10
resTableTypeSpecHd chunk size = 0x14
resTableTypeSpecHd id = 0x3
resTableTypeSpecHd entryCount = 0x1
.................
-mipmap ResTable_type count is:5
從結(jié)果中看出entryCount為1,mipmap類型的ResTable_type數(shù)量也為5,與其配置種類一樣。
然后按照前面假設(shè)的做法,在res/mipmap-hdpi-v4放置一個(gè)名為test.png的文件。重新編譯生成新的resources.arsc文件,再次運(yùn)行測(cè)試(加入了ResTable_type.entryCount):
if(strcmp((((uint8_t*)mTypeString)+mTypeEntries[resTableTypeHd->id-1]+2),"mipmap")==0){
num +=1;
printf("mipmap ResTable_type.entryCount = %d\n",resTableTypeHd->entryCount);
..........
........
resTableTypeSpecHd type = 0x202
resTableTypeSpecHd chunk hd size = 0x10
resTableTypeSpecHd chunk size = 0x18
resTableTypeSpecHd id = 0x3
resTableTypeSpecHd entryCount = 0x2
mipmap ResTable_type.entryCount = 2
mipmap ResTable_type.entryCount = 2
mipmap ResTable_type.entryCount = 2
mipmap ResTable_type.entryCount = 2
mipmap ResTable_type.entryCount = 2
..........
-mipmap ResTable_type count is:5
看到了吧,雖然只是在在res/mipmap-hdpi-v4放置一個(gè)名為test.png的文件,沒有在其他mipmap中放置,但是每個(gè)ResTable_type.entryCount同樣由1變?yōu)榱?。
也就是說ResTable_type.entryCount和ResTable_typeSpec.entryCount應(yīng)該保持一致,都為該類型資源項(xiàng)的個(gè)數(shù),如果一個(gè)類型的某個(gè)資源項(xiàng)只存在某一個(gè)配置文件夾下,那也算一個(gè)資源項(xiàng)。
ResTable_entry
到這里就只剩下ResTable_type結(jié)構(gòu)中的ResTable_entry了。這里面存儲(chǔ)了資源項(xiàng)的值和資源項(xiàng)的資源ID,可以理解為資源項(xiàng)的數(shù)據(jù)塊。
每一個(gè)ResTable_type結(jié)構(gòu)后面都會(huì)有若干ResTable_entry,至于ResTable_entry的數(shù)量,每個(gè)ResTable_type可能回有所不同。
還以前面res/mipmap-hdpi-v4放置一個(gè)名為test.png的文件,其它mipmap文件夾中不放為例,那么與mipmap-hdpi-v4對(duì)應(yīng)的ResTable_type結(jié)構(gòu)后面的ResTable_entry就有兩個(gè),其他mipmap對(duì)應(yīng)的ResTable_type后面只有一個(gè)ResTable_entry。但是所有的ResTable_type結(jié)構(gòu)后面跟著的ResTable_entry偏移數(shù)組元素?cái)?shù)都是一樣的,還是為2.
再來看一次ResTable_type:
struct ResTable_type
{
struct Resheader header;
enum {
NO_ENTRY = 0xFFFFFFFF
};
// 類型ID
uint8_t id;
// Must be 0.
uint8_t res0;
// Must be 0.
uint16_t res1;
// 該類型資源項(xiàng)的數(shù)量
uint32_t entryCount;
// 該資源項(xiàng)值在 ResTable_entry數(shù)據(jù)部分的中的偏移
uint32_t entriesStart;
// 該資源的配置.
ResTable_config config;
};
ResTable_type結(jié)構(gòu)后面緊跟一個(gè)大小為entryCount的uint32_t數(shù)組ResTable_entry偏移數(shù)組,每一個(gè)數(shù)組元素都用來描述一個(gè)資源項(xiàng)數(shù)據(jù)塊的偏移位置。
ResTable_type結(jié)構(gòu)中的entriesStart指明了ResTable_type后面的一系列的ResTable_entry的起始位置。
這里還有一個(gè)十分重要的的地方,前面說了資源項(xiàng)值分為兩大類:bag類值和非bag類值。
非bag類值的資源項(xiàng)數(shù)據(jù)塊是ResTable_entry,而bag類值資源項(xiàng)數(shù)據(jù)塊由ResTable_map_entry描述。
struct ResTable_entry
{
// Number of bytes in this structure.
uint16_t size;
enum {
FLAG_COMPLEX = 0x0001,
FLAG_PUBLIC = 0x0002,
FLAG_WEAK = 0x0004
};
//根據(jù)flags的不同,后面跟隨的數(shù)據(jù)也不相同:bag資源和非bag資源
//flags為1,則ResTable_entry是ResTable_map_entry
//資源項(xiàng)標(biāo)志位。如果是一個(gè)Bag資源項(xiàng),那么FLAG_COMPLEX位就等于1,并且在ResTable_entry后面跟有一個(gè)ResTable_map數(shù)組,
//否則的話,在ResTable_entry后面跟的是一個(gè)Res_value。如果是一個(gè)可以被引用的資源項(xiàng),那么FLAG_PUBLIC位就等于1。/
uint16_t flags;
//對(duì)應(yīng)的資源項(xiàng)名稱 資源項(xiàng)名稱字符串池中的偏移數(shù)組的索引
struct ResStringPool_ref key;
};
struct ResTable_map_entry : public ResTable_entry
{
ResTable_ref parent;
//bag類可取值的數(shù)量
uint32_t count;
};
struct ResStringPool_ref
{
uint32_t index;
};
對(duì)應(yīng)mipmap類型的ic_launcher來說,其值也不是事先就定義好的,所以是一個(gè)非bag類值??梢酝ㄟ^flags來驗(yàn)證。
對(duì)于非bag類值來說,ResTable_entry后面緊跟著一個(gè)Res_value結(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;
//數(shù)據(jù)的類型,可以從上面的枚舉類型中獲取
uint8_t dataType;
// The data for this item, as interpreted according to dataType.
// 對(duì)于bag類值來說,data就是其值
// 對(duì)于非bag類值來說,其值是在資源項(xiàng)值字符串池中偏移數(shù)組的索引
typedef uint32_t data_type;
data_type data;
void copyFrom_dtoh(const Res_value& src);
};
現(xiàn)在以代碼獲取ic_launcher的值來驗(yàn)證:
printf("#################### type spec信息 ####################\n");
ResTable_typeSpec *resTableTypeSpecHd = NULL;
ResTable_type *resTableTypeHd = NULL;
Resheader *chunk_hd = (Resheader *)((uint8_t*)keyStringPoolHd+keyStringPoolHd->header.size);
int num = 0;
for(int i=0;i<resTablePackageHd->lastPublicType;i++){
// 說明是TYPE_TYPE
while(chunk_hd->type == RES_TABLE_TYPE_TYPE){
resTableTypeHd = (ResTable_type *)chunk_hd;
if(strcmp((((uint8_t*)mTypeString)+mTypeEntries[resTableTypeHd->id-1]+2),"mipmap")==0){
num +=1;
printf("mipmap ResTable_type.entryCount = %d\n",resTableTypeHd->entryCount);
// 得到數(shù)組
uint32_t * su =(uint32_t *)(resTableTypeHd->header.headerSize+(uint8_t*)resTableTypeHd);
//ResTable_entry data starts
// 得到ResTable_entry起始位置
uint8_t * addr = (uint8_t*)((uint8_t*)resTableTypeHd+resTableTypeHd->header.headerSize+resTableTypeHd->entryCount*sizeof(uint32_t));
for(int i=0;i<resTableTypeHd->entryCount;i++){
// 因?yàn)槠茢?shù)組中元素?cái)?shù)量可能比其后面的ResTable_entry數(shù)量多,對(duì)于沒有對(duì)應(yīng)ResTable_entry結(jié)構(gòu)的偏移數(shù)組中元素,其值為0xffffffff.
if(su[i]!=0xffffffff){
// 依次加上偏移得到對(duì)應(yīng)的ResTable_entry
ResTable_entry * entry = (ResTable_entry *)(addr+su[i]);
// 如果flags是bag值類型的話,flags最低bit位為1
printf("entry flags:0x%x\n",entry->flags);
if(strstr(((char*)mKeyString)+mKeyEntries[entry->key.index],"ic_launcher")){
//ResTable_entry后面緊跟著Res_Value
Res_value* value = (Res_value*)((uint8_t*)entry+entry->size);
printf("value is:%s\n",((char*)mString)+mEntries[value->data]+2);
}
}
}
}
chunk_hd = (Resheader *)((uint8_t*)chunk_hd+chunk_hd->size);
}
resTableTypeSpecHd = (ResTable_typeSpec*)chunk_hd;
printf("resTableTypeSpecHd type = %p\n",resTableTypeSpecHd->header.type);
printf("resTableTypeSpecHd chunk hd size = %p\n",resTableTypeSpecHd->header.headerSize);
printf("resTableTypeSpecHd chunk size = %p\n",resTableTypeSpecHd->header.size);
printf("resTableTypeSpecHd id = %p\n",resTableTypeSpecHd->id);
// 頭部后面緊跟著數(shù)組元素個(gè)數(shù)為entryCount的uint32_t數(shù)組,每一個(gè)數(shù)組元素都用來描述一個(gè)資源項(xiàng)的配置差異性的。
printf("resTableTypeSpecHd entryCount = %p\n",resTableTypeSpecHd->entryCount);
chunk_hd = (Resheader *)((uint8_t*)chunk_hd+chunk_hd->size);
}
printf("-mipmap ResTable_type count is:%d\n",num);
結(jié)果:
...........
resTableTypeSpecHd type = 0x202
resTableTypeSpecHd chunk hd size = 0x10
resTableTypeSpecHd chunk size = 0x18
resTableTypeSpecHd id = 0x3
resTableTypeSpecHd entryCount = 0x2
mipmap ResTable_type.entryCount = 2
entry flags:0
value is:res/mipmap-mdpi-v4/ic_launcher.png
mipmap ResTable_type.entryCount = 2
entry flags:0
value is:res/mipmap-hdpi-v4/ic_launcher.png
entry flags:0
mipmap ResTable_type.entryCount = 2
entry flags:0
value is:res/mipmap-xhdpi-v4/ic_launcher.png
mipmap ResTable_type.entryCount = 2
entry flags:0
value is:res/mipmap-xxhdpi-v4/ic_launcher.png
mipmap ResTable_type.entryCount = 2
entry flags:0
value is:res/mipmap-xxxhdpi-v4/ic_launcher.png
.........
-mipmap ResTable_type count is:5
那么對(duì)于bag值類型,以前面介紹bag時(shí)列舉的類型:
在res/values中創(chuàng)建attrs.xml文件,在其中自定一個(gè)bag類型的屬性資源。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="custom_orientation">
<enum name="custom_vertical" value="100" />
<enum name="custom_horizontal" value="200" />
</attr>
</resources>
前面已經(jīng)知道了custom_orientation是由三個(gè)bag構(gòu)成的:
第一個(gè)bag:名稱是“^type”,值是TYPE_ENUM(TYPE_ENUM = 1<<16)
第二個(gè)bag:名稱是“custom_vertical”,值是100
第三個(gè)bag: 名稱是“custom_horizontal”,值是200
另外還要給這個(gè)bag分配資源ID:
名稱是“^type”的bag其分配的資源ID是attr類型的,而“custom_vertical”和“custom_horizontal”被分配到的資源ID是id類型的。
對(duì)于bag值的資源項(xiàng)數(shù)據(jù)塊是ResTable_map_entry:
struct ResTable_map_entry : public ResTable_entry
{
ResTable_ref parent;
//bag類可取值的數(shù)量
uint32_t count;
};
ResTable_map_entry后面跟著若干ResTable_map,確切來說一個(gè)Bag資源項(xiàng)有N個(gè)bag,那么在ResTable_map_entry后面就有N個(gè)ResTable_map。
/**
* A single name/value mapping that is part of a complex resource
* entry.
*/
struct ResTable_map
{
//等于bag的資源項(xiàng)ID。
ResTable_ref name;
// 等于bag的資源項(xiàng)值。
// 確切來說是Res_value.data
Res_value value;
};
代碼驗(yàn)證:
printf("#################### type spec信息 ####################\n");
ResTable_typeSpec *resTableTypeSpecHd = NULL;
ResTable_type *resTableTypeHd = NULL;
ResChunk_header *chunk_hd = (ResChunk_header *)((uint8_t*)keyStringPoolHd+keyStringPoolHd->chunk_header.size);
int num = 0;
for(int i=0;i<resTablePackageHd->lastPublicType;i++){
// 說明是TYPE_TYPE
while(chunk_hd->type == RES_TABLE_TYPE_TYPE){
resTableTypeHd = (ResTable_type *)chunk_hd;
if(strcmp((((uint8_t*)mTypeString)+mTypeEntries[resTableTypeHd->id-1]+2),"mipmap")==0){
num +=1;
printf("mipmap ResTable_type.entryCount = %d\n",resTableTypeHd->entryCount);
// 得到數(shù)組
uint32_t * su =(uint32_t *)(resTableTypeHd->chunk_header.headerSize+(uint8_t*)resTableTypeHd);
//ResTable_entry data starts
uint8_t * addr = (uint8_t*)((uint8_t*)resTableTypeHd+resTableTypeHd->chunk_header.headerSize+resTableTypeHd->entryCount*sizeof(uint32_t));
for(int i=0;i<resTableTypeHd->entryCount;i++){
if(su[i]!=0xffffffff){
ResTable_entry * entry = (ResTable_entry *)(addr+su[i]);
printf("entry flags:0x%x\n",entry->flags);
if(strstr(((char*)mKeyString)+mKeyEntries[entry->key.index],"ic_launcher")){
//ResTable_entry后面緊跟著Res_Value
Res_value* value = (Res_value*)((uint8_t*)entry+entry->size);
printf("value is:%s\n",((char*)mString)+mEntries[value->data]+2);
}
}
}
}
// 因?yàn)閏ustom_orientation本質(zhì)上是一個(gè)屬性資源,所以以attr來判斷
if(strcmp((((uint8_t*)mTypeString)+mTypeEntries[resTableTypeHd->id-1]+2),"attr")==0){
uint32_t * su =(uint32_t *)(resTableTypeHd->chunk_header.headerSize+(uint8_t*)resTableTypeHd);
//ResTable_entry data starts
uint8_t * addr = (uint8_t*)((uint8_t*)resTableTypeHd+resTableTypeHd->chunk_header.headerSize+resTableTypeHd->entryCount*sizeof(uint32_t));
for(int i=0;i<resTableTypeHd->entryCount;i++){
if(su[i]!=0xffffffff){
ResTable_map_entry * map_entry = (ResTable_map_entry *)(addr+su[i]);
if(strstr(((char*)mKeyString)+mKeyEntries[map_entry->key.index],"custom_orientation")){
printf("entry flags:0x%x\n",map_entry->flags);
// 等于本bag資源的可取值數(shù)量,也預(yù)示著后面緊跟著count個(gè)ResTable_map結(jié)構(gòu)
printf("bag count: %d\n",map_entry->count);
for(int j=0;j<map_entry->count;j++){
ResTable_map* map_value = (ResTable_map*)((uint8_t*)map_entry+map_entry->size);
ResTable_map* map_value1 = map_value+j;
printf("bag value-->%p\n",map_value1->value.data);
printf("bag id-->%p\n" , map_value1->name);
}
}
}
}
}
chunk_hd = (ResChunk_header *)((uint8_t*)chunk_hd+chunk_hd->size);
}
resTableTypeSpecHd = (ResTable_typeSpec*)chunk_hd;
printf("resTableTypeSpecHd type = %p\n",resTableTypeSpecHd->chunk_header.type);
printf("resTableTypeSpecHd chunk hd size = %p\n",resTableTypeSpecHd->chunk_header.headerSize);
printf("resTableTypeSpecHd chunk size = %p\n",resTableTypeSpecHd->chunk_header.size);
printf("resTableTypeSpecHd id = %p\n",resTableTypeSpecHd->id);
// 頭部后面緊跟著數(shù)組元素個(gè)數(shù)為entryCount的uint32_t數(shù)組,每一個(gè)數(shù)組元素都用來描述一個(gè)資源項(xiàng)的配置差異性的。
printf("resTableTypeSpecHd entryCount = %p\n",resTableTypeSpecHd->entryCount);
chunk_hd = (ResChunk_header *)((uint8_t*)chunk_hd+chunk_hd->size);
}
printf("-mipmap ResTable_type count is:%d\n",num);
結(jié)果:
#################### type spec信息 ####################
resTableTypeSpecHd type = 0x202
resTableTypeSpecHd chunk hd size = 0x10
resTableTypeSpecHd chunk size = 0x368
resTableTypeSpecHd id = 0x1
resTableTypeSpecHd entryCount = 0xd6
entry flags:0x1 //預(yù)示著是一個(gè)bag值類型
bag count: 3
bag value-->0x10000 //TYPE_ENUM = 1<<16
bag id-->0x1000000
bag value-->0xc8 //200
bag id-->0x7f0c0009
bag value-->0x64 //100
bag id-->0x7f0c000a
結(jié)果和預(yù)期都是符合的,否則使無法解析的。
這里特別指出一點(diǎn),ResTable_type后面的資源項(xiàng)數(shù)據(jù)塊可能既有ResTable_entry又有ResTable_map_entry,例如本例中資源類型attr的ResTable_type就是這種情況。
但ResTable_map_entry繼承自ResTable_entry。
ResTable_entry后面跟這個(gè)的是一個(gè)Res_value,而ResTable_map_entry后面跟著的是若干ResTable_map,數(shù)量是ResTable_map_entry.count決定。每個(gè)ResTable_map中都有一個(gè)Res_value。
好了到這里位置就徹底搞清楚resources.arsc的格式了。