给定一个相同的资源ID,在不同的设备配置之下,查找到的可能是不同的资源。这个资源查找过程对应用程序来说,是完全透明的。现在就详细分析资源管理框架是如何根据ID来查找资源的。
资源按照是否有文件可以分为两类:。第一类资源是不对应有文件的,例如字符串资源,而第二类资源是对应有文件的,例如drawable资源。
分别对这两种情况进行分析。
资源ID格式
前面的文章中已经介绍了资源ID格式,这里在啰嗦一遍。
资源ID是一个32位四字节数字,格式:PPTTEEEE。
其中PP代表package id.系统资源的package id为0x1,而app自己的资源包package id为0x7f。0x1与0x7f之间的package id都合法。
TT代表资源的类型(type);
NNNN代表这个类型下面的资源项的名称(entry);
TT 和NNNN 的取值是由aapt工具随意指定的——基本上每一种新的资源类型的数字都是从上一个数字累加的(从1开始);而每一个新的资源entry条目也是从数字1开始向上累加的。
假设3个资源文件按照下面的顺序排列:
layout/main.xml
drawable/icon.xml
layout/listitem.xml
aapt会依次处理:
按照顺序,第一个资源的类型是”layout” 所以指定TT=1, 这个类型下面的第一个资源是”main” ,所以指定NNNN=1 ,最后这个资源就是0x7f010001。
第二个资源类型是”drawable”,所以指定TT=2,这个类型下的”icon” 指定NNNN =1,所以最终的资源ID 是 0x7f020001。
第三个资源类型是”layout”,而这个资源类型在前面已经有定义了,所以TT仍然是1,但是”listitem”这个名字是新出现的,所以指定NNNN=2,因此最终的资源ID 就是 0x7f010002。
也就是说NNNN是可以重复的,因为可以结合TT来区分。
从资源ID获取字符串资源
下面的代码用来获得一个app的名字。以此为入口点来分析字符串资源的查找过程。
String appName = getResources().getString(R.string.app_name);
R.string.app_name是一个资源ID。
整个过程如下所示:
resources-8.jpg
其中第6步返回的Restable对象,通过上一篇的介绍已经知道了,它里面包含了本app所有的resoureces.arsc。
重点关注ResTable的getReouces方法:
ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
uint32_t* outSpecFlags, ResTable_config* outConfig) const
{
if (mError != NO_ERROR) {
return mError;
}
// 得到package id:PP
const ssize_t p = getResourcePackageIndex(resID);
// 得到 type : TT
const int t = Res_GETTYPE(resID);
// 得到 entry: EEEE
const int e = Res_GETENTRY(resID);
if (p < 0) {
......................
return BAD_INDEX;
}
if (t < 0) {
................
return BAD_INDEX;
}
// 得到packageGroup,里面有package
// 标准Android中app的资源包中只会包含一个package ,id为0x7f
// 而系统资源的package id 为 0x1
// 也就是说PackageGroup虽然可以包含多个package,但实际上只会包含一个package
const PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
return BAD_INDEX;
}
// Allow overriding density
// 配置相关
ResTable_config desiredConfig = mParams;
if (density > 0) {
desiredConfig.density = density;
}
Entry entry;
// 实际上就是根据grp,t,e在resources.arsc索引资源
status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
if (err != NO_ERROR) {
................
return err;
}
// 根据flags判断是否是flags资源,这是处理非bag资源的
if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
if (!mayBeBag) {
ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
}
return BAD_VALUE;
}
// ResTable_entry后面跟着的就是Res_value
const Res_value* value = reinterpret_cast<const Res_value*>(
reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
outValue->size = dtohs(value->size);
outValue->res0 = value->res0;
// 得到数据类型
outValue->dataType = value->dataType;
// 得到数据在字符串值池中索引数组的索引
outValue->data = dtohl(value->data);
if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
ALOGW("Failed to resolve referenced package: 0x%