Android6.0之App中的資源Rsources.arsc詳解

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)于資源打包過程以及查找的圖:

資源打包.jpg

從上圖可以看出:

  1. 除了assets和res/raw資源被原裝不動(dòng)地打包進(jìn)APK之外,其它的資源都會(huì)被編譯或者處理.xml文件會(huì)被編譯為二進(jìn)制的xml,所以解壓apk后,無法直接打開xml文件。

  2. 除了assets資源之外,其它的資源都會(huì)被賦予一個(gè)資源ID。

  3. 打包工具負(fù)責(zé)編譯和打包資源,編譯完成之后,會(huì)生成一個(gè)resources.arsc文件和一個(gè)R.java,前者保存的是一個(gè)資源索引表,后者定義了各個(gè)資源ID常量,供在代碼中索引資源。

  4. 應(yīng)用程序配置文件AndroidManifest.xml同樣會(huì)被編譯成二進(jìn)制的XML文件,然后再打包到APK里面去。

  5. 應(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ù)塊。文件格式:

AMS-33.png

這張神圖在網(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ī)則的:

  1. 與字符串格式是string8還是string16相關(guān)

  2. 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)度)。

  1. 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的格式了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 給定一個(gè)相同的資源ID,在不同的設(shè)備配置之下,查找到的可能是不同的資源。這個(gè)資源查找過程對(duì)應(yīng)用程序來說,是完全透明...
    小爨閱讀 2,642評(píng)論 1 9
  • Android與資源管理相關(guān)的類Resouces和AssetManager很有必要清楚他們的創(chuàng)建過程。 與資源查找...
    小爨閱讀 3,508評(píng)論 4 14
  • Android插件化基礎(chǔ)的主要內(nèi)容包括 Android插件化基礎(chǔ)1-----加載SD上APKAndroid插件化基...
    隔壁老李頭閱讀 7,388評(píng)論 13 48
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評(píng)論 19 139
  • 插件化-資源處理 寫的比較長(zhǎng),可以選擇跳過前面2節(jié),直接從0x03實(shí)例分析開始。如有錯(cuò)誤,請(qǐng)不吝指正。 0x00 ...
    唐一川閱讀 5,782評(píng)論 2 22

友情鏈接更多精彩內(nèi)容