参考网址:http://blog.csdn.net/luoshengyang/article/details/8744683
在Android资源中,有一种资源类型称为Public,它们一般是定义在res/values/public.xml文件中,形式如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <public type="string" name="string3" id="0x7f040001" />
- </resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="string" name="string3" id="0x7f040001" />
</resources>
这个public.xml用来告诉Android资源打包工具aapt,将类型为string的资源string3的ID固定为0x7f040001。为什么需要将某一个资源项的ID固定下来呢?一般来说,当我们将自己定义的资源导出来给第三方应用程序使用时,为了保证以后修改这些导出资源时,仍然保证第三方应用程序的兼容性,就需要给那些导出资源一个固定的资源ID。
每当Android资源打包工具aapt重新编译被修改过的资源时,都会重新给这些资源赋予ID,这就可能会造成同一个资源项在两次不同的编译中被赋予不同的ID。这种情况就会给第三方应用程序程序带来麻烦,因为后者一般是假设一个ID对应的永远是同一个资源的。因此,当我们将自己定义的资源导出来给第三方应用程序使用时,就需要通过public.xml文件将导出来的资源的ID固定下来。
我们用一个例子来说public.xml文件的作用,考虑下面这个strings.xml文件:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <string name="string1">String 1</string>
- <string name="string3">String 3</string>
- </resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="string1">String 1</string>
<string name="string3">String 3</string>
</resources>
假设Android资源打包工具aapt为字符串资源项string1和string3分配到的资源ID如下所示:
- public final class R {
- // ...
- public static final class string {
- public static final int string1=0x7f040000;
- public static final int string3=0x7f040001;
- }
- }
public final class R {
// ...
public static final class string {
public static final int string1=0x7f040000;
public static final int string3=0x7f040001;
}
}
这时候第三方应用程序就会认为0x7f040001引用的永远是字符串“String 3”。
假设将来的某一天,我们需要在strings.xml文件中增加一个新的字符串,如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <string name="string1">String 1</string>
- <string name="string2">String 2</string>
- <string name="string3">String 3</string>
- </resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="string1">String 1</string>
<string name="string2">String 2</string>
<string name="string3">String 3</string>
</resources>
如果没有上述的public.xml文件,那么Android资源打包工具aapt为字符串资源项string1、 string2和string3分配的资源ID就会如下所示:
- public final class R {
- // ...
- public static final class string {
- public static final int string1=0x7f040000;
- public static final int string2=0x7f040001;
- public static final int string3=0x7f040002; // New ID! Was 0x7f040001
- }
- }
public final class R {
// ...
public static final class string {
public static final int string1=0x7f040000;
public static final int string2=0x7f040001;
public static final int string3=0x7f040002; // New ID! Was 0x7f040001
}
}
这就完蛋了,这时候第三方应用程序通过0x7f040001引用到的字符串变成了“String 2”。
如果我们使用上述的public.xml文件将字符串“String 3”固定为0x7f040001,那么Android资源打包工具aapt为字符串资源项string1、 string2和string3分配的资源ID就会如下所示:
- public final class R {
- // ...
- public static final class string {
- public static final int string1=0x7f040000;
- public static final int string2=0x7f040002;
- public static final int string3=0x7f040001; // Resource ID from public.xml
- }
- }
public final class R {
// ...
public static final class string {
public static final int string1=0x7f040000;
public static final int string2=0x7f040002;
public static final int string3=0x7f040001; // Resource ID from public.xml
}
}
这样第三方应用程序通过0x7f040001引用到的字符串仍然是“String 3”。
注意,我们在开发应用程序时,一般是不需要用到public.xml文件的,因为我们的资源基本上都是在内部使用的,不会导出来给第三方应用程序使用。只在内部使用的资源,不管它的ID如何变化,我们都可以通过R.java文件定义的常量来正确地引用它们。只有系统定义的资源包才会使用到public.xml文件,因为它定义的资源是需要提供给第三方应用程序使用的。
Step 2. 写入类型字符串资源池
在前面的第1个操作中,我们已经将每一个Package用到的类型字符串收集起来了,因此,这里就可以直接将它们写入到Package资源项元信息数据块头部后面的那个数据块去。
Step 3. 写入资源项名称字符串资源池
在前面的第2个操作中,我们已经将每一个Package用到的资源项名称字符串收集起来了,这里就可以直接将它们写入到类型字符串资源池后面的那个数据块去。
Step 4. 写入类型规范数据块
类型规范数据块用来描述资源项的配置差异性。通过这个差异性描述,我们就可以知道每一个资源项的配置状况。知道了一个资源项的配置状况之后,Android资源管理框架在检测到设备的配置信息发生变化之后,就可以知道是否需要重新加载该资源项。类型规范数据块是按照类型来组织的,也就是说,每一种类型都对应有一个类型规范数据块。
类型规范数据块的头部是用一个ResTable_typeSpec来定义的。ResTable_typeSpec定义在文件frameworks/base/include/utils/ResourceTypes.h中,如下所示:
- /**
- * 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
- * configuation 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
- };
- };
/**
* 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
* configuation 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里面的ResChunk_header的各个成员变量的取值如下所示:
--type:等于RES_TABLE_TYPE_SPEC_TYPE,用来描述一个类型规范头部。
--headerSize:等于sizeof(ResTable_typeSpec),表示头部的大小。
--size:等于sizeof(ResTable_typeSpec) + sizeof(uint32_t) * entryCount,其中,entryCount表示本类型的资源项个数。
ResTable_typeSpec的其它成员变量的取值如下所示:
--id:表示资源的Type ID。
--res0:等于0,保留以后使用。
--res1:等于0,保留以后使用。
--entryCount:等于本类型的资源项个数,注意,这里是指名称相同的资源项的个数。
ResTable_typeSpec后面紧跟着的是一个大小为entryCount的uint32_t数组,每一个数组元数,即每一个uint32_t,都是用来描述一个资源项的配置差异性的。例如,在图17中,名称为icon的drawable资源项有三种不同的屏幕配置ldpi、mdpi和hdpi,于是用来描述它的配置差异性的uint32_t的第CONFIG_DENSITY位就等于1,而其余位都等于0。又如,在图17中,名称为main的layout资源项只有一种配置default,于是用来描述它的配置差异性的uint32_t的值就等于0。此外,如果一个资源项是导出的,即它的资源ID是通过public.xml来固定的,那么用来描述它的配置差异性的uint32_t的第ResTable_typeSpec::SPEC_PUBLIC位也会被设置为1。