資源管理的基本資料結構
上一篇我們介紹了resources.arsc以及與之相關的主要資料結構,這些資料結構大多以ResTable_開頭,主要是用來描述resources.arsc非常友善。不過,要用這些資料結構來對Android資源進行管理,還是有些吃力,Android為了更加友善地管理資源,還有另外一套資料結構,它們主要是
ResTable
和
ResTable
的内部類。
與resources.arsc相關的資料結構當中,最主要的是
ResTable_package
、
ResTable_type
、
ResTable_entry
這三級架構,也和資源id形式0xpptteeee(pp表示最高的一個位元組表示包id,第二高的一個位元組表示type id,最後兩個位元組表示entry id)相一緻。但是,要對實作對這三級資源架構的管理,要依靠的絕對不僅僅是這三級。确切地說,Android資源管理中用到,但是resources.arsc中沒有用到資料結構,主要是
ResTable
、
ResTable::PackageGroup
、
ResTable::Package
、
ResTable::TypeList
、
ResTable::Type
、
ResTable::Entry
。其中,
ResTable::Package
、
ResTable::Entry
和
ResTable_package
、
ResTable_entry
對應;
ResTable::Type
和
ResTable_type
的意義完全不一樣;
ResTable
、
ResTable::PackageGroup
、
ResTable::TypeList
是resources.arsc中沒有類似的資料結構與之對應。我們這裡再着重說一下,
ResTable_
開頭的資料結構偏重于對resources.arsc的描述,而
ResTable::
系列的資料結構是用來對資源資訊進行管理的,AssetManager中的許多接口功能的實作都要依賴它們。
ResTable
Android資源管理架構-------之Android中的資源包(二)中,我們知道一個AssetManager可能會加載許多資源包,包括Android系統資源包也就是framework-res.apk、Soc廠商資源包、手機廠商資源包以及App本身,甚至還有可能存在overlay包、資源共享庫等等。那麼這些資源包中的resources.arsc加載後被存到了哪裡呢?最終它們都會被存放到一個
ResTable
的對象中,并且這個對象是native層
AssetManager
的一個成員。也就是說,一個
ResTable
對象會存儲它所在的AssetManager加載的所有資源包中的resources.arsc,并且對這些resources.arsc做統一的管理,這一點我們一定要注意。
//frameworks/base/include/androidfw/ResourceTypes.h
class ResTable
{
//......省略無關代碼
private:
/**
* 目前裝置配置資訊,我們去擷取資源時,要根據它來選擇合适的資源
* 我們給AssetManager設定或者更新配置資訊時,最終也會存在這裡
*/
ResTable_config mParams;
/**
* 存放我們加載的所有資源包中的resources.arsc
* 當然它不會把整個resources.arsc都放進來,隻是放入了每個resources.arsc的header資訊
* 這樣通過header我們可以通路整個resources.arsc
*/
Vector<Header*> mHeaders;
//我們加載過來的資源包相關的資訊都會放到這個資料結構中
Vector<PackageGroup*> mPackageGroups;
/**
* 這是一個Map,key為下标,具體為加載的package的id,
* value為,這個package在mPackageGroups中的索引 + 1
*/
uint8_t mPackageMap[256];
//配置設定給下一個資源共享庫的臨時PackageId
uint8_t mNextPackageId;
}
我們看到一個内部,存儲的關鍵資訊就三個:
mParams
存儲配置資訊、
mHeaders
存儲加載的resources.arsc、mPackageGroups存儲加載的資源包相關的資訊。如果不考慮Runtime Resources Overlay,一個id為
packageId
的資源包的資訊為
mPackageGroups[mPackageMap[packageId] - 1]
。另外,在resources.arsc中,資源共享庫的packageId都是0,是以加載的時候要為它們動态配置設定一個id,mNextPackageId就是做這個用的。
ResTable::PackageGroup
按照一般的邏輯,一個
ResTable
對象中直接存放一個
ResTable::Package
的集合就可以了,為什麼我們看到的卻是存放了一個
ResTable::PackageGroup
的集合呢?并且從
ResTable::PackageGroup
的實作來看它的内部确确實實又是存在多個
ResTable::Package
的,這個怎麼了解呢?
//frameworks/base/lib/androidfw/ResourceTypes.cpp
struct ResTable::PackageGroup
{
//......省略無關代碼
//這個PackageGroup所屬的ResTable
const ResTable* const owner;
/**
* 這個PackageGroup的name,為這個PackageGroup中第一個Package的name
*/
String16 const name;
uint32_t const id;
//這個PackageGroup内的所有Package
Vector<Package*> packages;
/**
* 每一個元素都代表一種類型的資源
* 當我們通路資源時,是通過PackageGroup到TypeList,再到Type,再到Entry
* 中間并不會經過上面的packages變量
*/
ByteBucketArray<TypeList> types;
//最大typeId
uint8_t largestTypeId;
//這個PackageGroup所有的bag資源,這個後面會單講,現在可以先無視之
ByteBucketArray<bag_set**>* bags;
//資源共享庫相關的資料結構
DynamicRefTable dynamicRefTable;
}
其實,
ResTable::PackageGroup
的存在,是為了Runtime Resources Overlay(簡稱RRO),一般情況下一個
ResTable::PackageGroup
裡隻會有一個
ResTable::Package
,包括我們的應用包(id:0x7f)、系統資源包(id:0x01)、Soc廠商資源包、手機廠商資源包以及資源共享庫包等等。但是有RRO時,系統會把target包和overlay包放到同一個
ResTable::PackageGroup
中,以便擷取資源時可以快速地定位overlay包中的資源。并且target包會被放到
packages[0]
,overlay包則會放到後面,這個
ResTable::PackageGroup
的包名和id都會以target包為準。
ResTable::TypeList
//frameworks/base/include/androidfw/ResourceTypes.h
typedef Vector<Type*> TypeList;
ResTable::TypeList
的定義非常簡單。我們前面說過,通路資源時,通過
ResTable::PackageGroup
,到
ResTable::TypeList
,再到
ResTable::Type
,再到
ResTable::Entry
。我們看到,
ResTable::PackageGroup
的
types
成員是一個數組,這個數組中的每一個元素也就是
ResTable::TypeList
代表一種特定類型的資源,比如drawable、string等。但是,
ResTable::TypeList
本身又是一個
ResTable::Type
類型的數組,這個我們又該怎麼了解呢?
ResTable::TypeList
裡的每一個
ResTable::Type
對應一個不同的配置嗎? No!實際情況是這樣的:
ResTable::TypeList
和
ResTable::PackageGroup
類似,也是為了處理Runtime Resources Overlay。一般情況下,它的裡面也是隻有一個元素的,當有RRO的時候,我們可以通過下面的例子來了解:
//target package
<resources>
......
<string name="app_greeting">Goog Morning</string>
<string name="app_appologize">Sorry</string>
......
</resources>
// overlay package
<resources>
<string name="app_greeting">Goog Night</string>
</resources>
在這個例子中,我們試圖通過overlay package來改變字元串
app_greeting
的值。那麼,在加載完target package後,overlay package也會被加載到 target package所在的ResTable中,并且會和target package放到同一個
ResTable::PackageGroup
中。并且,overlay package中的所有string類型的資源都會被系統放到target package中的string類型的資源所在的
ResTable::TypeList
中。也就是說 overlay package和target package中相同類型的資源,會被放到同一個
ResTable::TypeList
中,隻不過,target package中的資源會放在頭一個元素的位置,overlay package的放到後面。這樣做,可以很友善地把target package和overlay package中的資源組織到一起,通路也友善。
ResTable::Package
//frameworks/base/lib/androidfw/ResourceTypes.cpp
struct ResTable::Package
{
//......省略無關代碼
//所屬的ResTable
const ResTable* const owner;
//對應的resources.arsc
const Header* const header;
//對應的resources.arsc中的ResTable_package
const ResTable_package* const package;
// Type String Pool
ResStringPool typeStrings;
// Key String Pool
ResStringPool keyStrings;
//......省略無關代碼
}
通常一個ResTable::Package就會對應一個resources.arsc了。并且它裡面還會記錄這個resources.arsc中關鍵的資料塊,比如
ResTable_package
、Type String Pool、Key String Pool等。
ResTable::Type
ResTable::Type
的意義和
ResTable_type
完全不同。我們前文講過
ResTable_type
表示resources.arsc中某種配置下的某類型的所有資源,而
ResTable::Type
則表示一個包中某種類型的所有資源。注意,是某種類型的所有資源,包括所有配置!其實從這個角度來講,它倒是更像
ResTable_typeSpec
,我們看看
ResTable::Type
的實作:
//frameworks/base/lib/androidfw/ResourceTypes.cpp
struct ResTable::Type
{
Type(const Header* _header, const Package* _package, size_t count)
: header(_header), package(_package), entryCount(count),
typeSpec(NULL), typeSpecFlags(NULL) { }
//所屬的resources.arsc
const Header* const header;
//所屬的資源包
const Package* const package;
//資源項的個數
const size_t entryCount;
//資源對應的ResTable_typeSpec
const ResTable_typeSpec* typeSpec;
//flags
const uint32_t* typeSpecFlags;
//RRO相關的資料結構
IdmapEntries idmapEntries;
//表示不同配置下的所有資源了
Vector<const ResTable_type*> configs;
};
我們看到,一個
ResTable::Type
中包含多個
ResTable_type
,并且對于這個
ResTable_type
集合對象的命名也叫configs。自然,它裡面的每一個元素就對應一種配置了。
ResTable::Entry
ResTable::Entry
和
ResTable_entry
的差別倒不大,隻不過是對後者又做了進一步的封裝,添加了它所屬的類型、Package等資訊。
//frameworks/base/lib/androidfw/ResourceTypes.cpp
struct ResTable::Entry {
//所屬ResTable_type的配置
ResTable_config config;
//對應的ResTable_entry
const ResTable_entry* entry;
//所屬的ResTable_type
const ResTable_type* type;
uint32_t specFlags;
//所屬的Package
const Package* package;
//所屬的類型的名字在Type String Pool中的索引
StringPoolRef typeStr;
//該資源項的名字在Key String Pool中的索引
StringPoolRef keyStr;
};
另外,還有幾個資料結構也值得一說,先看
ResTable::Header
:
//frameworks/base/lib/androidfw/ResourceTypes.cpp
struct ResTable::Header
{
Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL),
resourceIDMap(NULL), resourceIDMapSize(0) { }
~Header()
{
free(resourceIDMap);
}
//所屬的ResTable對象
const ResTable* const owner;
//用來存儲整個resources.arsc,預設情況下這個是NULL,不會使用
void* ownedData;
//用來存儲整個resources.arsc的頭部
const ResTable_header* header;
//整個resources.arsc中資料的大小
//size = header->size
size_t size;
//整個resources.arsc的結尾位址
const uint8_t* dataEnd;
/**
* 該resources.arsc或者說該資源包在owner對象中的索引
* 或者說是該header對象在ResTable::mHeaders中的索引
* 都是一會事兒
*/
size_t index;
/**
* 該資源包是owner對象加載的第幾個資源包,從1開始
* 一般cookie = index + 1
*/
int32_t cookie;
//resources.arsc的Key String Pool
ResStringPool values;
//資源共享庫相關
uint32_t* resourceIDMap;
//資源共享庫相關
size_t resourceIDMapSize;
};
我們看到
ResTable::Header
内部的資料結構,大都是resources.arsc相關的,也就是說,它主要用來在資源管理的過程中來描述一個resources.arsc,而通常一個資源包對應一個resources.arsc,是以也可以了解為它和一個資源包是一一對應的。這裡的index成員好了解,cookie我們簡單說明以下。在Android的資源管理架構中cookie的出現頻率極高,不過它們的含義基本一緻,都是代表一個資源包或者說一個resources.arsc或者說一個Key String Pool(它們三個是一一對應的)在它所屬的
ResTable
中的索引值(從1開始計數)。畢竟一個AssetManager也好,一個
ResTable
也好,它的内部都會存在多個資源包,當我們傳回一個資源資訊時,少不了要傳回這個資源所屬的資源包,這個就是cookie值了。
再看下面兩個資料結構:
//frameworks/base/include/androidfw/ResourceTypes.h
struct ResTable_ref
{
uint32_t ident;
};
struct ResStringPool_ref
{
uint32_t index;
};
首先這兩個資料結構不僅在進行資源管理的時候會用到,在resources.arsc中也會用到。其中
ResTable_ref
,表示一項資源的引用,32位整型值就是形如0xpptteeee的一個id,跟R檔案中的id一模一樣,也就是說,它實際上指向了另一個資源的id。從這裡我們也可以看到,android的資源管理是支援資源引用的,這在我們使用資源的時候将會非常非常友善,但缺點是,當我們擷取資源時,如果結果的類型是TYPE_REFERENCE,我們擷取到的将不會是最終的結果,如果想得到最終的具體值,則需要增加對引用的解析這個步驟。而
ResStringPool_ref
則是一個字元串在
ResStringPool
中的索引。通常當我們要通路的資源是一個字元串的時候,Android資源管理架構的native層不會直接傳回一個字元串,而是傳回一個
ResStringPool_ref
和一個cookie值,前者表示索引,後者表示結果字元串哪個
ResStringPool
中,然後我們根據索引到對應的
ResStringPool
中就可以拿到具體的結果了。
我們畫一張圖來描述相關的資料結構:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSP9cmTycGRPVTR6pVd5cVZoJlMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zROBlLyMjM2QDMwAjMyETOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
我們看到,這張圖中少了
Restable::Entry
,因為它可以看做是對
Restable:_entry
的封裝,它的存在是為了我們從上層更加友善地get資源。不過,它卻是是遊離于
ResTable
、
ResTable::PackageGroup
、
ResTable::Package
、
ResTable::TypeList
、
ResTable::Type
之外的,它們都沒用引用
Restable::Entry
。
AssetManager
AssetManager
是native層資源管理的接口,它實際上是對
ResTable的封裝
,我們可以粗略地認為,
AssetManager
負責加載資源包,并從中解壓出各種資源檔案,特别是resources.arsc,然後将它丢給
ResTable
,後面的事兒就由
ResTable
負責了。
AssetManager
在管理資源包的過程中用到的
AssetDir
、
Asset
、
_FileAsset
、
_CompressedAsset
等資料結構,我們可以看做是對一般目錄、檔案、壓縮包的封裝;
AssetManager::SharedZip
、
AssetManager::ZipSet
,則是為了友善我們對APK包的通路以及緩存。這些資料結構算是資源管理的工具吧,它們和資源管理本身關系不大,我們這裡一句帶過了。
//frameworks/base/include/androidfw/AssetManager.h
#ifdef __cplusplus
extern "C" {
#endif
struct AAssetManager { };
#ifdef __cplusplus
};
#endif
class AssetManager : public AAssetManager {
//.....省略次要代碼
//加載的資源包的路徑
Vector<asset_path> mAssetPaths;
//ResTable
mutable ResTable* mResources;
//配置資訊
ResTable_config* mConfig;
//.....省略次要代碼
}
到這裡,Android資源管理相關的底層的主要資料結構我們已經介紹的差不多了,當然Runtime Resources Overlay、資源共享庫相關的資料結構我們沒有介紹,如果大家有興趣,點開連接配接可以詳細了解它們的實作和原理。還有就是XML相關的資料結構,它們比較獨立,和Android資源管理關系不是那麼不可分割,有機會我們可以搞個專題。在這裡我們注意區分開
ResTable_
和
ResTable::
打頭的資源即可:前者大多用來描述resources.arsc;後者用于資源管理的邏輯的實作。當然
ResTable_config
、
ResTable_ref
、ResStringPool等,在描述resources.arsc和管理資源時都會用到。
Bag資源
我們前面講資源項的時候說到,一般情況下,一個資源項其實是一個鍵值對兒:鍵表示資源項的名字,用
ResTable_entry
表示;值表示資源項的相關資訊(具體值或者資源的路徑等等),用
Res_value
表示。但是還有二般情況,有一些資源我們僅僅用一個鍵值對兒是描述不清的,比如style類型的資源:
<resources>
.......
<style name="WorkspaceIcon">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
<item name="android:textSize">@dimen/workspace_icon_text_size</item>
<item name="android:textColor">@color/workspace_icon_text_color</item>
<item name="android:shadowRadius">2.0</item>
<item name="android:shadowColor">#B0000000</item>
</style>
<style name="WorkspaceIcon.Portrait">
<item name="android:drawablePadding">@dimen/app_icon_drawable_padding</item>
<item name="android:paddingStart">4dp</item>
<item name="android:paddingEnd">4dp</item>
<item name="android:paddingTop">@dimen/app_icon_padding_top</item>
<item name="android:paddingBottom">4dp</item>
</style>
......
</resources>
我們看style
WorkspaceIcon.Portrait
,抛開它的parent style
WorkspaceIcon
不談,它的名稱似乎可以用一個
ResTable_entry
來表示,但是它的值我們可以用一個
Res_value
表示嗎?顯然不能!!!
這個style的值不是一個簡單的值,而是由5個鍵值對兒組成:
android:drawablePadding ---> @dimen/app_icon_drawable_padding
android:paddingStart ---> 4dp
android:paddingEnd ---> 4dp
android:paddingTop ---> @dimen/app_icon_padding_top
android:paddingBottom ---> 4dp
對于類似style的這種資源,既然不能使用
ResTable_entry
+
Res _value
來表示,那就隻能使用新的資料結構了,也就是
ResTable_map_entry
和
ResTable_map
:
//frameworks/base/include/androidfw/ResourceTypes.h
struct ResTable_map_entry : public ResTable_entry
{
// Resource identifier of the parent mapping, or 0 if there is none.
// This is always treated as a TYPE_DYNAMIC_REFERENCE.
ResTable_ref parent;
// Number of name/value pairs that follow for FLAG_COMPLEX.
uint32_t count;
};
我們看到
ResTable_map_entry
繼承了
ResTable_entry
,并且添加了兩個新的字段,分别來表示它的父style和item的個數。
//frameworks/base/include/androidfw/ResourceTypes.h
struct ResTable_map
{
//表示鍵值對兒中的鍵
ResTable_ref name;
//......省略次要代碼
//表示鍵值對兒中的值
Res_value value;
}
在一個resources.arsc中,一個
ResTable_map_entry
後面會跟
ResTable_map_entry::count
個
ResTable_map
。具體到這個例子當中:
ResTable_map_entry::size = 16 /*16個位元組*/
//FLAG_COMPLEX表示這個結構體後面跟的不再是簡單的Res_value,而是一個個的mapping
ResTable_map_entry::flags = FLAG_COMPLEX
//key 是我們這個style的名字,當然這裡是WorkspaceIcon.Portrait在key string pool中的索引
ResTable_map_entry::key = "WorkspaceIcon.Portrait"在key string pool中的索引
//parent 當然是WorkspaceIcon這個style了
ResTable_map_entry::parent = WorkspaceIcon這個style的id
//後面跟的鍵值對兒的個數,也就是style的item的個數
ResTable_map_entry::count = 5
//後面會有5個ResTable_map,它們的值分别為:
/*ResTable_map::value的類型是Res_value, 我們為了友善,在這裡隻寫出了其data字段,沒有列出其data_type等其它字段*/
ResTable_map::name = android:drawablePadding的id ResTable_map::value = dimen/app_icon_drawable_padding的id
ResTable_map::name = android:paddingStart的id ResTable_map::value = 4dp
ResTable_map::name = android:paddingEnd的id ResTable_map::value = 4dp
ResTable_map::name = android:paddingTop的id ResTable_map::value = dimen/app_icon_padding_top的id
ResTable_map::name = android:paddingBottom的id ResTable_map::value = 4dp
以上的結構非常好了解,我們用一個一個的鍵值對兒來描述一項比較複雜的資源,這每一個鍵值對兒就叫該資源的一個Bag。也就是說,style的每一個item都是style的Bag。我們的style
WorkspaceIcon.Portrait
自身有5個Bag,它的parent style
WorkspaceIcon
有10個Bag,并且不存在覆寫的情況,是以style
WorkspaceIcon.Portrait
總共有15個Bag。
當然,擁有Bag的資源不隻style一種,除此之外,還有bag、plurals、array、string-array、integer-array、attr共7種。
bag類型的資源隻在aapt中有定義,無論是Android 系統源碼還是一般應用當中都還沒有發現使用,這裡就不說了。
array、string-array、integer-array這三種資源差不多可以看成一種。一個array同樣會包含多個item,比如:
<array name="sim_colors">
<item>@color/Teal_700</item>
<item>@color/Blue_700</item>
<item>@color/Indigo_700</item>
<item>@color/Purple_700</item>
<item>@color/Pink_700</item>
<item>@color/Red_700</item>
</array>
大家有沒有發現和style不一樣的地方?style的每一個item都是一個鍵值對兒,但是array的每一個item都隻有值,并沒有鍵!!!array的item表面上确實沒有鍵,但其實它是有的,那就是索引,也就是每一個item在array中的位置或者說順序。并且aapt在編譯array(包括string-array和integer-array)的時候,會同時記錄其索引的,并且這個索引用^index_%d來表示,其中%d表示索引值,比如0、 1 、2…
plurals大家應該也經常接觸,它主要用來處理多語言環境下,字元串的單複數形式:
<plurals name="copy_begin">
<item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
<item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
</plurals>
比如這個,
quantity
的值自然就是key了,android中
quantity
的值總共可以取:^zero、 ^one、^two、^few、^many、^other六種。當然,不同的語言環境可能隻用其中的某一些,比如英語,隻有單複數,是以隻用^one、^other兩種,這個和具體的語言相關,這裡就不多說了。
attr我們大家都非常熟悉了,它也有Bag資源?一個形如
<attr name="textAppearance" format="reference" />
的attr,一個
ResTable_entry
和一個
Res_value
似乎是夠了的。我們不急,先來看看這個attr:
<attr name="orientation">
<!-- Defines an horizontal widget. -->
<enum name="horizontal" value="0" />
<!-- Defines a vertical widget. -->
<enum name="vertical" value="1" />
</attr>
這個就是Android源生的orientation屬性的定義,它是一個枚舉類型,這時候一個
ResTable_entry
+ 一個
Res_value
顯然是不行的。其實,形如
<attr name="textAppearance" format="reference" />
的attr,用一個
ResTable_entry
+ 一個
Res_value
也是不行的,因為它的鍵值可以是這個屬性的名字textAppearance,但是值呢?顯然是某個資源的id,而不能是
reference
。事實上,format=“reference” 這一句,我們可以看作是對
textAppearance
這個attr的描述。當然,
<enum name="horizontal" value="0" />
和也一樣是對
orientation
這個attr的描述,表示它隻的值隻能是二者之一,而不能随意去取。aapt在編譯的時候,會把format也作為一個Bag記錄下來,表示這個attr的類型,并用^type來表示。我們再來看看另外完整版的
ResTable_map
:
//frameworks/base/include/androidfw/ResourceTypes.h
struct ResTable_map
{
ResTable_ref name;
enum {
ATTR_TYPE = Res_MAKEINTERNAL(0),
ATTR_MIN = Res_MAKEINTERNAL(1),
ATTR_MAX = Res_MAKEINTERNAL(2),
ATTR_L10N = Res_MAKEINTERNAL(3),
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)
};
enum {
TYPE_ANY = 0x0000FFFF,
TYPE_REFERENCE = 1<<0,
TYPE_STRING = 1<<1,
TYPE_INTEGER = 1<<2,
TYPE_BOOLEAN = 1<<3,
TYPE_COLOR = 1<<4,
TYPE_FLOAT = 1<<5,
TYPE_DIMENSION = 1<<6,
TYPE_FRACTION = 1<<7,
TYPE_ENUM = 1<<16,
TYPE_FLAGS = 1<<17
};
enum {
L10N_NOT_REQUIRED = 0,
L10N_SUGGESTED = 1
};
Res_value value;
};
我們看到,它裡面有三個枚舉類型的資料,它們是做什麼的呢?最後一個顯然是本地化相關的,我們不做讨論。我們先說第一個,當我們的資源是attr的時候,它們可以作為
ResTable_map::name
的值,用來描述這個attr相關的元資訊。比如,這個屬性的類型、最大取值、最取小值等等,當
ResTable_map::name
的值為
ATTR_TYPE
的時候,Res_value::data的值該是什麼呢?答案就是第二個enum中的值;當我們的資源是plurals的時候,它的Bag的key隻能是^zero、 ^one、^two、^few、^many、^other,值就是item當中的字元串;當我們的資源是array(包括string-array和integer-array)的時候,它的Bag的key隻能是^index_%d.
Bag資源相關的東西我們就先介紹到這裡,後面講aapt的時候我們還會詳細講Bag資源的編譯。這裡我們隻需要知道什麼是Bag資源就可以了。