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

栏目: IOS · Android · 发布时间: 5年前

内容简介:上篇博客写到,Package资源剩下的部分是由多组RES_TABLE_TYPE_SPEC_TYPE和RES_TABLE_TYPE_TYPE构成的。一个RES_TABLE_TYPE_SPEC_TYPE后面跟着一个或者多个RES_TABLE_TYPE_TYPE构成一种类型的资源的描述(例如string类型、bool类型、dimen类型等)我们接着来看看RES_TABLE_TYPE_SPEC_TYPE的头部结构体:

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

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

RES_TABLE_TYPE_SPEC_TYPE

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

/**
 * A specification of the resources defined by a particular type.
 *
 * There should be one of these chunks for each resource type.
 *
 * This structure is followed by an array of integers providing the set of
 * configuration change flags (ResTable_config::CONFIG_*) that have multiple
 * resources for that configuration.  In addition, the high bit is set if that
 * resource has been made public.
 */
struct ResTable_typeSpec
{
    struct ResChunk_header header;

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

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

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

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

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

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

// Flags indicating a set of config values.  These flag constants must
// match the corresponding ones in android.content.pm.ActivityInfo and
// attrs_manifest.xml.
enum {
    CONFIG_MCC = ACONFIGURATION_MCC,
    CONFIG_MNC = ACONFIGURATION_MCC,
    CONFIG_LOCALE = ACONFIGURATION_LOCALE,
    CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN,
    CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD,
    CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN,
    CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION,
    CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,
    CONFIG_DENSITY = ACONFIGURATION_DENSITY,
    CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE,
    CONFIG_SMALLEST_SCREEN_SIZE = ACONFIGURATION_SMALLEST_SCREEN_SIZE,
    CONFIG_VERSION = ACONFIGURATION_VERSION,
    CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
    CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
    CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
};

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

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

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

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

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

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

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

//printStringFromStringsPool:

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


//main:

...

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

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

...

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

...
type:id=3,name=bool
80
0
0

...

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

//configuration.h
ACONFIGURATION_ORIENTATION = 0x0080,

//ResTable_config里面的enum
CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,

让我们返回去对比下:

// res/values目录下
<bool name="abc_action_bar_embed_tabs">true</bool>
<bool name="abc_allow_stacked_button_bar">false</bool>
<bool name="abc_config_actionMenuItemAllCaps">true</bool>

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

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

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

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

RES_TABLE_TYPE_TYPE

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

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

/**
 * A collection of resource entries for a particular resource data
 * type. Followed by an array of uint32_t defining the resource
 * values, corresponding to the array of type strings in the
 * ResTable_package::typeStrings string block. Each of these hold an
 * index from entriesStart; a value of NO_ENTRY means that entry is
 * not defined.
 *
 * There may be multiple of these chunks for a particular resource type,
 * supply different configuration variations for the resource values of
 * that type.
 *
 * It would be nice to have an additional ordered index of entries, so
 * we can do a binary search if trying to find a resource by string name.
 */
struct ResTable_type
{
    struct ResChunk_header header;

    enum {
        NO_ENTRY = 0xFFFFFFFF
    };

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

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

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

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

    ResTable_config config;
};

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

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

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

...
type: id=0x3,name=bool
80
0
0
config : 
config : port
...

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

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

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

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

/**
 * This is the beginning of information about an entry in the resource
 * table.  It holds the reference to the name of this entry, and is
 * immediately followed by one of:
 *   * A Res_value structure, if FLAG_COMPLEX is -not- set.
 *   * An array of ResTable_map structures, if FLAG_COMPLEX is set.
 *     These supply a set of name/value mappings of data.
 */
struct ResTable_entry
{
    // Number of bytes in this structure.
    uint16_t size;

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

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


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

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

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

ResTable_entry

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

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

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

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

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

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

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

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

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

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

    // Always set to 0.
    uint8_t res0;

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

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

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

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

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

        // ...end of integer flavors.
        TYPE_LAST_INT = 0x1f
    };
    uint8_t dataType;
    
     // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION)
    enum {
        // Where the unit type information is.  This gives us 16 possible
        // types, as defined below.
        COMPLEX_UNIT_SHIFT = 0,
        COMPLEX_UNIT_MASK = 0xf,

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

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

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

        // The mantissa is an integral number -- i.e., 0xnnnnnn.0
        COMPLEX_RADIX_23p0 = 0,
        // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn
        COMPLEX_RADIX_16p7 = 1,
        // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn
        COMPLEX_RADIX_8p15 = 2,
        // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn
        COMPLEX_RADIX_0p23 = 3,
        
        // Where the actual value is.  This gives us 23 bits of
        // precision.  The top bit is the sign.
        COMPLEX_MANTISSA_SHIFT = 8,
        COMPLEX_MANTISSA_MASK = 0xffffff
    };

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

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

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

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

type: id=0x3,name=bool
80
0
0
config :
entryIndex: 0x0, key :
abc_action_bar_embed_tabs
value :
(boolean) true

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

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

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

ResTable_map_entry

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

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

<attr name="buttonTintMode">
	<enum name="src_over" value="3"/>
	<enum name="src_in" value="5"/>
	<enum name="src_atop" value="9"/>
	<enum name="multiply" value="14"/>
	<enum name="screen" value="15"/>
	<enum name="add" value="16"/>
</attr>

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

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

我们来看看struct ResTable_map_entry:

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

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

资源的id

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

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

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

public final class R {
	...
	public static final class bool {
	    public static final int abc_action_bar_embed_tabs=0x7f030000;
	    public static final int abc_allow_stacked_button_bar=0x7f030001;
	    public static final int abc_config_actionMenuItemAllCaps=0x7f030002;
	  }
	...
}

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

0xpptteeee

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

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

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

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

type: id=0x3,name=bool
80
0
0
config :
entryIndex: 0x0, key :
abc_action_bar_embed_tabs
value :
(boolean) true

entryIndex: 0x1, key :
abc_allow_stacked_button_bar

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

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

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

...

type: id=0x4,name=color

...

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

...

ResTable_map

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

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

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代表的就是这个键值对的key,而它的value代表的就是键值对的值。

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

#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF))

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

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

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

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

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

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

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

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

第一个ResTable_ref的name的indent的值是0x1000000,就代表name是ATTR_TYPE,也代表这个资源是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

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

DOM Scripting

DOM Scripting

Jeremy Keith / friendsofED / 2010-12 / GBP 27.50

There are three main technologies married together to create usable, standards-compliant web designs: XHTML for data structure, Cascading Style Sheets for styling your data, and JavaScript for adding ......一起来看看 《DOM Scripting》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换