今天來說說Android的資源索引系統, Android的資源編譯使用aapt工具, 查找則使用AssertManager.
Android如何來索引資源呢,首先我們從一個應用程序來找資源進行分析.
Android中通過id來進行資源的查找, 那麼怎麼通過id來查找呢
我們對id進行分割, id是一個32位的數字, 前8位爲packageid, 後面8爲爲typeid,最後16位是在該type下的資源索引.
首先根據packageid找到資源索引文件,也就是resources.arsc文件. 根據在根據type找到對應的type集合, type類型一般有drawable, layout, anim等等, 再根據id的後16爲找到對應id描述的資源, 最後根據當前系統配置,選擇最合適的資源,
Aapt
AaptDir: 描述一個類型的文件夾,比如 drawable,drawable-xxxdpi都對應drawler AaptDir, Aaptdir中存放多個AaptGroup
AaptGroup: 則描述AaptDir下相同名稱的文件, 比如drawable/a.xml drawable-xxxdpi/a.xml, 都放在同一個AaptGroup下, 每個文件對應一個AaptFile
AaptFile: 則具體描述了一個資源文件.
aapt編譯的過程首先就是組織文件,也就是描述AaptDir->AaptFile的樹狀結構, 之後編譯文件.也就是對文件名稱進行索引.
之後編譯values文件夾下的資源生成索引
之後編譯xml
剩下的就是寫文件,寫R.java
編譯的過程其實就是將AaptDir->AaptFile轉換爲
Type
ConfigList
Entry的過程
Type對應AaptDir 描述一類資源如 drawable,drawable-xxxdpi 都屬於drawable類型資源,一個type下有多個ConfigList
ConfigList: 描述相同id資源的不同屬性資源如 xxdpi xdpi屬於不同屬性, 索引最終要就是找到ConfigList, 每個ConfigList下的不同屬性相同id的資源用Entry描述
Entry:描述一個資源的值以及配置, 系統會根據配置選擇ConfigList的Enrty
assets->slurpFromArgs(bundle) 收集文件.
AaptDir 表示一個文件夾
AaptFile對應一個文件, AaptFile 的mGroupEntry成員變量類型爲 AaptGroupEntry,代表文件的屬性信息,如 drawler-xxhdpi , 屬性信息爲xxhdpi
AaptGroup 爲一組同名不同屬性的文件
編譯資源步驟
parsePackage 從Manifest解析出包名
ResourceTable.addIncludedResources 添加引用資源 如android.jar
collect_files 收集資源文件
applyFileOverlay 執行overlay
makeFileResources 收集資源項
compileResourceFile 編譯values 文件
ResourceTable.assignResourceIds() 分配資源id
Entry->generateAttributes(this, p->getName()) 給bag資源分配id
Entry->assignResourceIds(this, p->getName()) 給bag資源分配到的id保存到bagKeyId 中
compileXmlFile 編譯xml文件
XMLNode.assignResourceIds 給xml的attribute分配id
XMLNode.parseValues 給xml的屬性設置value
XMLNode.flatten 壓平
XMLNode.collect_resid_strings 收集屬性字符串,屬性id
XMLNode.collect_strings 收集字符串
寫xml頭, 寫字符串池塊,寫id池塊
XMLNode.flatten_node 寫node塊
XMLNode.flatten_node 壓平node
compileXmlFile 編譯AndroidManifest.xml文件
ResourceTable.addSymbols 生成R文件內容
ResourceTable.flatten 生成包級信息
StringPool valueStrings(useUTF8) 值字符串池,全局
StringPool typeStrings(useUTF8); 類型字符串池. 包級
StringPool keyStrings(useUTF8); 資源名字符串池, 包級
configTypeName = “1complex”; 編譯類型
configTypeName = “2value”; 值類型
Package.setTypeStrings(typeStrings.createStringBlock()); 收集typestrings
Package.setKeyStrings(keyStrings.createStringBlock()); 收集keystrings
1 收集typeStrings, keyStrings ,valueStrings
2Entry.prepareFlatten(&valueStrings, this,
&configTypeName, &config) 收集keystrings
3 寫RES_TABLE_PACKAGE_TYPE 頭文件ResTable_package 每個包對應一個
4 寫typestrings
5 寫keystrings
6 寫依賴庫信息RES_TABLE_LIBRARY_TYPE ResTable_lib_header
7 寫RES_TABLE_TYPE_SPEC_TYPE ResTable_typeSpec 用於描述每種type下每個資源支持的配置信息,通過uint32_t類型數類描述支持的配置信息
8 寫RES_TABLE_TYPE_TYPE ResTable_type 用於描述每種type下每種配置下對應的資源項, 也即沒重類型的每個配置對應一個ResTable_type. 如下圖, 該結構分爲三部分,分別是頭部, 描述資源具體信息的偏移信息,以及資源的具體信息 ResTable_entry. 所謂ResTable_entry其實保存的就是Res_Value(對於數字類型的Res_value->data爲具體的數字, 對於字符串類型的Res_value->data爲字符串池中的索引值,文件名其實也是字符串類型, 對於引用類型Res_value->data保存id值), 對於bag類型則保存bag信息. 這裏不展開bag
寫入RES_TABLE_TYPE ResTable_header 到resources.arsc
寫如 valuestrings字符串池
寫入ResourceTable.flatten 生成的包級信息
寫R.java文件
所以總結一下resources.arsc的格式
ResTable_header
StringPool: valuestrings
PackageInfo: 0 … package_size
ResTable_package
StringPool: typestrings
StringPool: keystrings
ResTable_lib_header
0…type_size
ResTable_typeSpec 0…config_size
ResTable_type 0…config_size
attribute_entry 描述xml的一個屬性
struct attribute_entry {
String16 ns; xml屬性的命名空間
String16 name; 屬性名
String16 string; // 值字符串
Res_value value; // 解析後的值
uint32_t index; // 第幾個屬性
uint32_t nameResId; 屬性對應的id
mutable uint32_t namePoolIdx; // 在字符串池中的索引
};
Res_value 描述一個值
struct Res_value
{
// Number of bytes in this structure.
uint16_t size; // 結構提佔用的內存大小
// Always set to 0.
uint8_t res0; // 總是設置成0
uint8_t dataType; 數據類型
// The data for this item, as interpreted according to dataType.
typedef uint32_t data_type;
data_type data; // 數據值
};
對於數字類型的Res_value->data爲具體的數字, 對於字符串類型的Res_value->data爲字符串池中的索引值,文件名其實也是字符串類型, 對於引用類型Res_value->data保存id值
class Entry{
private:
String16 mName; // 名字
String16 mParent; // 父名字
type mType; // 類型 TYPE_ITEM 和TYPE_BAG
Item mItem; // 當類型爲TYPE_ITEM 使用該item存放value
int32_t mItemFormat; // entry表示的類型
KeyedVector<String16, Item> mBag; // TYPE_BAG 描述該資源下的bag,其實每個bag也是一個Item
int32_t mNameIndex; // 字符串池中的索引
uint32_t mParentId;
SourcePos mPos;
};
class Type {
private:
String16 mName; //名字
SourcePos* mFirstPublicSourcePos;
DefaultKeyedVector<String16, Public> mPublic;
DefaultKeyedVector<String16, sp<ConfigList> > mConfigs; //資源同名項
Vector<sp<ConfigList> > mOrderedConfigs; ///排序同名資源項
SortedVector<String16> mCanAddEntries;
int32_t mPublicIndex;
int32_t mIndex; //第幾項
SourcePos mPos;
}
class Package {
const String16 mName; //包名
const size_t mPackageId; // id
DefaultKeyedVector<String16, sp<Type> > mTypes; // 支持的類型
Vector<sp<Type> > mOrderedTypes; // 排序後的類型
sp<AaptFile> mTypeStringsData; // 類型字符串池文件
sp<AaptFile> mKeyStringsData; // 名稱字符串池文件
ResStringPool mTypeStrings; // 類型字符串池
ResStringPool mKeyStrings;// 名稱字符串池
DefaultKeyedVector<String16, uint32_t> mTypeStringsMapping;
DefaultKeyedVector<String16, uint32_t> mKeyStringsMapping;
}
所以resource的編譯過程包括兩部分, 分別是xml編譯和resources.arsc 字符串索引
最重要的數據結構 Res_value , Entry, Item , attribute_entry, StringPool