可能是全網(wǎng)講最細(xì)的安卓resources.arsc解析教程(二)

上篇博客寫到,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ù)的地址俺陋。

1.png

那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:

2.png

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)舒一口氣,終于大功告成茄茁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末魂贬,一起剝皮案震驚了整個(gè)濱河市巩割,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌付燥,老刑警劉巖宣谈,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異键科,居然都是意外死亡闻丑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門勋颖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事怪得。” “怎么了徒恋?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵蚕断,是天一觀的道長(zhǎng)入挣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)径筏,這世上最難降的妖魔是什么葛假? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮滋恬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘带斑。我一直安慰自己勋拟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布挂滓。 她就那樣靜靜地躺著,像睡著了一般杂彭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上所计,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天团秽,我揣著相機(jī)與錄音,去河邊找鬼踪栋。 笑死图毕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的囤官。 我是一名探鬼主播蛤虐,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼刑顺!你這毒婦竟也來(lái)了饲常?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贯城,失蹤者是張志新(化名)和其女友劉穎霹娄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體犬耻,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枕磁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茸苇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淘衙,死狀恐怖腻暮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情具垫,我是刑警寧澤试幽,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站起宽,受9級(jí)特大地震影響康震,放射性物質(zhì)發(fā)生泄漏宾濒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一橘忱、第九天 我趴在偏房一處隱蔽的房頂上張望卸奉。 院中可真熱鬧,春花似錦凝颇、人聲如沸疹鳄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至川无,卻和暖如春虑乖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背决左。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工佛猛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人继找。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓婴渡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哄尔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子柠并,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345