我們平時都是用 AS 進行打包,這就造成了很多盲點,我們就來看看究竟是咋回事,提前宣告這篇文章講的不全,只講一些疑惑盲點,需要全面學習的,看老羅的吧,詳細的令人髮指。
我們從結果入手,看看打包完畢的apk裡面是啥模樣,把.apk 修改成 .zip 解壓縮。按圖索驥哈哈。
一共五個檔案,第一個 AndroidManifest.xml 我們很熟悉了。開啟看一下。 我的個乖乖,咋回事。 然後在開啟 res 檔案,很多維度的 drawable 資料夾啊。 隨便開啟一個吧,看看裡面的 xml 檔案,nm,咋也都是二進位制資料了呢?好吧,這些貌似是和 資源編譯打包有關係哈,印象裡好像是這麼回事,趕緊百度,谷歌。看到了如下的神圖貌似和我們遇到的類似啊。
aapt 這傢伙,把我們的資源搞了一下。他想幹什麼呢?帶著疑問我們去學習。1、為啥要把文字格式的xml 轉換成 二進位制格式的
為啥呢?如果猜測的話,應該不是閒的沒事搞得,我們知道安卓面臨的問題一直是空間和時間的問題,這麼搞,無非就是省空間,提升速度。
2、怎麼做呢?
把所有 xml 元素的標籤,屬性,內容等字串統一放到一個 字串資源池裡面去,然後在用到這個字串的地方用索引來代替,這是偷懶的行家啊,也是計算機裡的區域性思想的發揚光大。這樣就可以解決空間佔用的大小了。 那麼怎麼就速度快了呢?因為這裡的字串用的是索引,所以就不必每次都解析字串了,這還不快嗎?重複利用多開心啊。
ok,這個 xml 二進位制的問題也就解決完畢。但是一個問題的結束往往伴隨著另一個問題的開始。那個字串資源池在哪裡呢?
這就引出了我們的上面 五大部分的 Resources.arsc。先容我百度,谷歌下哈。 blog.csdn.net/beyond702/a… 很顯然我不是要解析 Resources.arsc,反正我知道了,這個字串資源池就在 Resources.arsc 中,名字叫 Global String Pool,這樣就可以了。
好了回到 aapt 這個傢伙。
據網上總結他有以下幾個重要的工作。
1、 assert 和 res/raw 目錄下的所有資源原封不動打包到 apk 裡。 2、對 res/ 目錄下的檔案進行編譯處理 比如 xml 編譯成二進位制檔案,png 等圖片也會進行優化處理。 3、除了 assert 資源之外的所有資源都會賦予一個資源ID常量,並且聲稱一個資源索引表 Resources.arsc。 4、把 AndroidManifest.xml 也進行二進位制處理。 5、把上面四步驟中聲稱的結果儲存到一個*.ap_ 檔案,把各個資源 ID 常量定義在 R.java 檔案中。
這麼一來解答了不少疑惑,但是 *.ap_ 是個啥玩意呢?下圖是網上的,說實話,我反正沒看到,我實驗了下沒有 .ap 檔案。咋辦呢?繼續搜吧,可能是文章有點老了。
還是看官方文件吧,我擦,appt2 了啊,好吧,看英文文件使我快樂(?)。文件上說 appt 已經棄用,開始使用 appt2 了,雖然老專案也在用,但是你懂得。
developer.android.com/studio/comm…
終於找到了一篇文章,講述 appt2 編譯的。原來是 appt2 將原來的資源編譯打包過程拆分成了兩部分,編譯和連結,提高了資源的編譯效能。當只有一個資源發上改變的時候,只需要重新編譯改變的檔案就行了。其他不變的進行連結就行了。之前 appt 是將所有的資源進行merge ,merge 完畢重新對所有資源進行編譯,產生一個 資源 ap_ 檔案。這個也就是一個壓縮包。
具體細節大家有興趣可以搞一搞。我心裡還是有點不明白這些個過程,所有又找了一篇文章來看看。
這個的圖真是詳細的很啊。
我們要編譯的應用程式的資源結構目錄。圖文結合一下,不然不知道說的什麼。
第一步:解析AndroidManifest.xml
獲得包名,根據包名建立資源表 ResourceTable 。 那什麼是 ResourceTable 呢?Android資源打包工具在編譯應用資源之前,會建立一個資源表,當編譯完成後,就可以拿著這個資源表,去生成資源索引檔案 resources.arsc。
第二步:新增被引用資源包
不光我們自己的應用擁有資源包,android 系統也定義了一套通用資源。所以需要把這個也新增上。最重要的的 資源ID 的命名規則是這樣的。一共四位,Package ID,次高位元組表示Type ID,最低兩位元組表示Entry ID。
Package ID:比如系統的就是0x01 ,我們自己的就是0x7f。
Type ID:資源的型別有animator、anim、color、drawable、layout、menu、raw、string和xml等等若干種,每一種都會被賦予一個ID。
Entry ID:是指每一個資源在其所屬的資源型別中所出現的次序。注意,不同型別的資源的Entry ID有可能是相同的,但是由於它們的型別不同,我們仍然可以通過其資源ID來區別開來。
總結就是 先按包名來分,然後看型別,最後看順序。
第三步:收集資原始檔
在編譯應用程式之前,aapt 會建立一個 AaptAsserts 物件。用來收集當前需要編譯的資原始檔。儲存到 AaptAsserts 的成員變數 mRes。
KeyedVector<String8, sp<ResourceTypeSet> >* mRes;
複製程式碼
收集資源是按照型別來儲存的 分別是 drawable、layout、values。所以對應了三種 ResourceTypeSet。
舉個例子說明下吧(其實我就是抄的)
1、型別是 drawalbe 的ResourceTypeSet 只有一個AaptGroup,它的名稱是 icon.png。這個AaptGroup 裡包含了三個檔案 res/drawable-ldpi/icon.png、res/drawable-mdpi/icon.png和res/drawable-hdpi/icon.png。 每一個檔案都用一個 AaptFile 來描述,並且都對應一個 AaptGroupEntry。每個 AaptGroupEntry 描述的都是不同的資源配置資訊,即他們所描述的螢幕密度是ldpi、mdpi和hdpi。
2、型別是 layout 的的ResourceTypeSet 有兩個AaptGroup,分別是 main.xml 和 sub.xml。都只包含了一個 AaptFile ,分別是res/layout/main.xml和res/layout/sub.xml。同樣分別對應一個AaptGroupEntry。這兩個AaptGroupEntry描述的資源配置資訊都是屬於default的。
3、型別為 values 的ResourceTypeSet 只有一個 AaptGroup,為 string.xml。包含了一個 AaptFile 即 res/values/strings.xml。同樣對應一個AaptGroupEntry,這個AaptGroupEntry描述的資源配置資訊也是屬於default的。
第四步:將收集到的資源新增到資源表
上一步只是儲存到 AaptAsserts 物件裡,我們需要將這些資源同時增加到 ResourceTable 物件中,為啥子呢?因為我們要用 ResourceTable 來生成 resources.arsc。這樣看來思路就有點清晰了。
需要注意的是: 收集的資源不包括 values 型別的資源,它比較特殊,要經過編譯才會新增到資源表中。(ps:又增加了一個問題)
舉個例子:
在這個名稱為“shy.luo.activity”的Package中,分別包含有drawable和layout兩種型別的資源,每一種型別使用一個Type物件來描述,其中:
1、型別是 drawable 的Type,包含一個 ConfigList。名稱為 icon.png。包含了三個 Entry,分別是res/drawable-ldpi/icon.png、res/drawable-mdpi/icon.png和res/drawable-hdpi/icon.png。每一個 Entry 對應一個 ConfigDescription,用來描述不同的資源配置資訊。
第五步:編譯 values 類資源
型別為 values 終於開始要編譯了,之前的疑問看來要在這裡進行解答了。我們通常用 values 來描述一些簡單的值,比如 顏色,大小,尺寸等等。這些資源是在編譯的過程中收集的。具體怎麼收集看下邊。
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Activity</string>
<string name="sub_activity">Sub Activity</string>
<string name="start_in_process">Start sub-activity in process</string>
<string name="start_in_new_process">Start sub-activity in new process</string>
<string name="finish">Finish activity</string>
</resources>
複製程式碼
這個檔案經過編譯後,資源表中會多了一個名為 string 的 Type。這個 Tpye 還有五個 ConfigList 。這五個 ConfigList 的名稱分別為 “app_name”、“sub_activity”、“start_in_process”、“start_in_new_process”和“finish”,每一個ConfigList又分別含有一個Entry。
六、給Bag資源分配ID
型別 values的資源除了 string 之外,還會有 bag,style,array 等。統一稱為 Bag 資源。比如 Android 系統提供的android:orientation屬性的取值範圍為{“vertical”、“horizontal”},就相當於是定義了vertical和horizontal兩個Bag。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="custom_orientation">
<enum name="custom_vertical" value="0" />
<enum name="custom_horizontal" value="1" />
</attr>
</resources>
複製程式碼
看完了 Bag 的解釋。我們看看是如何分配的ID的。上面是三個Entry均為 Bag 資源。其中 custom_vertical(id型別資源)和custom_horizontal( id型別資源)是custom_orientation(attr型別資源)的兩個bag。我們可以將custom_vertical和custom_horizontal看成是custom_orientation的兩個後設資料,用來描述custom_orientation的取值範圍。實際上,custom_orientation 還有一個內部後設資料,用來描述它的型別。這個內部後設資料也是通過一個 bag 來表示的,這個 bag 的名稱和值,分別是“^type”和TPYE_ENUM ,用來表示他描述的一個列舉型別的屬性。注意:所有“^”開頭的bag都是表示一個內部後設資料。
對於 Bag 資源來說,這一步需要給他們的後設資料項分配資源ID,也就是給他們的bag分配資源ID,例如上述的 custom_orientation 來說,我們需要給它的 ^type 、custom_horizontal 和 custom_vertical 分配資源ID。其中 ^type 分配到的是 attr 型別的資源ID,而custom_vertical和custom_horizontal分配的是 id 型別的資源ID。
第七步:編譯xml資原始檔
前六步都是為了編譯xml資原始檔做準備。不容易啊。
開始編譯:
除了 values 型別的資原始檔,其他所有xml資原始檔都需要編譯。以 main.xml 為例。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<Button
android:id="@+id/button_start_in_process"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/start_in_process" >
</Button>
<Button
android:id="@+id/button_start_in_new_process"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/start_in_new_process" >
</Button>
</LinearLayout>
複製程式碼
1、解析xml 檔案 這個就不多說了,最後得到根節點的 XMLNode。
2、賦予屬性名稱資源ID。
比如根節點 LinearLayout 裡面有“android:orientation”、“android:layout_width”、“android:layout_height”和“android:gravity”都需要賦予一個資源ID。這就給是在系統資源包裡定義的。所以AAPT會從系統資源包裡找到這些名稱對應的資源ID,然後才能賦給main.xml 的根節點LinearLayout。
注意:對於系統資源包來說“android:orientation”、“android:layout_width”、“android:layout_height”等這些屬性名稱都是它定義的一系列 Bag資源。在被編譯的時候就分配好資源ID了。如第六步。
每一個xml檔案都是從根節點開始給屬性名稱賦予資源ID的,然後在遞迴給每一個子節點的屬性名稱賦予資源ID。直到都獲得為止。 3、解析屬性值。 上一步只是對屬性的解析,這一步是對屬性值的解析。比如 main.xml 檔案的根節點 LinearLayout 來說,我們已經給他的屬性 android:orientation 賦值了一個資源 ID,這裡就是要給他的值 vertical 進行解析。上面說到了,android:orientation 是 Bag 資源,這個 Bag 資源分配有資源 ID,也會有後設資料,也就是它的取值。對於 android:orientation 的合法取值就是 horizontal 或者 vertical 。而這兩個也是 Bag 資源。他們的值分別被定義為 0 和 1。
AAPT 是如何找到 main.xml ->LinearLayout-> android:orientation->vertical 等於 1 的呢?假設上一步從系統資源找到資源 ID 0x010100c4,那麼 AAPT 會找到它的後設資料,也就是 名為 horizontal 和 vertical 的 Bag。接著根據字串匹配到 vertical 的 Bag,最後就可以將這個 Bag 解析了。
講個很基礎的一個東西,平時經常用,但是基本不會注意的一個點,對於引用型別的屬性值,比如 android:id屬性值“@+id/button_start_in_process”,其中 @ 表示後面描述的屬性是因引用型別的。+ 表示如果該引用不存在那麼就新建一個。id 表示引用的資源型別是 id。button_start_in_process 是個名稱。實際上在 id 之前還可以加 包名,@+[package:]id/button_start_in_process 就是這樣的,如果不指定那就從當前的包裡查詢。
再舉個例子,比如 android:text屬性值“@string/start_in_process”,在第五步的時候已經編譯過了,所以在這裡可以直接獲取他們的資源 ID。
注意:一個資源項一旦建立之後,要獲得它的 ID 是很容易的,因為它的 package id、tpye id和 entry id都是已知的。
4、壓平xml 這個詞很新鮮啊,第一次聽說這麼個東西,就是對xml檔案的內容進行扁平化處理,實際就是將xml檔案格式轉換成二進位制格式。過程如下圖。
一共分六步,(mmp好複雜) step 1、收集有資源 ID 的屬性的名稱字串 這一步除了會收集 資源 ID的屬性的名稱字串之外,還會將對應的資源ID收集到一個陣列中,這裡收集到的屬性名稱字串儲存在一個字串資源池中。他們與收集到的資源ID是一一對應的。也就是下圖這樣子滴。
step 2、收集其他字串 看到第一步我還在納悶,怎麼收集的字串就只有屬性的呢?原來還有其他的也放入字串資源池裡,不過對於字元不會重複收集,畢竟是字典嘛。
step 3、寫入XML檔案頭 最終編譯出來的XML二進位制檔案是一系列的chunk組成,每一個chunk都有一個頭部,用來描述chunk的元資訊,同時整個xml檔案又可以看成一個總的chunk。它有一個型別為 ResXMLTree_header的頭部。
struct ResChunk_header
{
uint16_t type;
uint16_t headerSize;
uint32_t size;
};
struct ResXMLTree_header
{
struct ResChunk_header header;
};
複製程式碼
--type:等於RES_XML_TYPE,描述這是一個Xml檔案頭部。
--headerSize:等於sizeof(ResXMLTree_header),表示頭部的大小。
--size:等於整個二進位制Xml檔案的大小,包括頭部headerSize的大小。
step 4、寫入字串資源池 原來定義在xml檔案中的字串已經在 1、2步收集完畢,因此,我們可以將它們寫入最終收集到二進位制格式的xml檔案中。寫入的順序必須嚴格按照在字串資源池中的寫入順序。例如,對於main.xml來說,依次寫入的字串為“orientation”、“layout_width”、“layout_height”、“gravity”、“id”、"text"、"android"、“schemas.android.com/apk/res/and… step 1 收集到的資源 ID 陣列也要寫入二進位制格式的xml中,保持這個資源ID 和字串資源池對應字串的對應關係。 寫入的字串池chunk同樣也是具有一個頭部的,這個頭部的型別為ResStringPool_header,它定義在檔案frameworks/base/include/utils/ResourceTypes.h中,如下所示:
struct ResStringPool_header
{
struct ResChunk_header header;
// Number of strings in this pool (number of uint32_t indices that follow
// in the data).
uint32_t stringCount;
// Number of style span arrays in the pool (number of uint32_t indices
// follow the string indices).
uint32_t styleCount;
// Flags.
enum {
// If set, the string index is sorted by the string values (based
// on strcmp16()).
SORTED_FLAG = 1<<0,
// String pool is encoded in UTF-8
UTF8_FLAG = 1<<8
};
uint32_t flags;
// Index from header of the string data.
uint32_t stringsStart;
// Index from header of the style data.
uint32_t stylesStart;
};
複製程式碼
--type:等於RES_STRING_POOL_TYPE,描述這是一個字串資源池。
--headerSize:等於sizeof(ResStringPool_header),表示頭部的大小。
--size:整個字串chunk的大小,包括頭部headerSize的大小。
ResStringPool_header的其餘成員變數的值如下所示:
--stringCount:等於字串的數量。
--styleCount:等於字串的樣式的數量。
--flags:等於0、SORTED_FLAG、UTF8_FLAG或者它們的組合值,用來描述字串資源串的屬性,例如,SORTED_FLAG位等於1表示字串是經過排序的,而UTF8_FLAG位等於1表示字串是使用UTF8編碼的,否則就是UTF16編碼的。
--stringsStart:等於字串內容塊相對於其頭部的距離。
--stylesStart:等於字串樣式塊相對於其頭部的距離。
step 6、寫入資源ID 這些收集到的資源ID會作為一個單獨的chunk寫入到最終的xml二進位制檔案中。這個chunk位於字串資源池的後面。它的頭部使用ResChunk_header來描述。這個ResChunk_header的各個成員變數的取值如下所示:
--type:等於RES_XML_RESOURCE_MAP_TYPE,表示這是一個從字串資源池到資源ID的對映頭部。
--headerSize:等於sizeof(ResChunk_header),表示頭部大小。
--size:等於headerSize的大小再加上sizeof(uint32_t) * count,其中,count為收集到的資源ID的個數。
以main.xml為例,字串資源池的第一個字串為“orientation”,而在資源ID這個chunk中記錄的第一個資料為0x010100c4,那麼就表示屬性名稱字串“orientation”對應的資源ID為0x010100c4。 step 6、壓平xml 壓平xml就是將各個xml元素中的字串都替換掉。要麼被替換成字串資源池的一個索引,要麼是被替換成一個具有型別的其他值。我們以main.xml 為例。
首先壓平的是一個表示名稱空間的xml node。這個Xml Node用兩個ResXMLTree_node和兩個ResXMLTree_namespaceExt來表示,如圖所示:
反正就是嗶哩嗶哩一大堆的約定協議引數,我就不多說了,感興趣大家就看老羅的文章研究下。反正就是xml各種稀裡糊塗的定規矩,解析什麼的。八、生成資源符號
這些生成的資源符號為後面生成R.java 做準備。所有的資源項按照型別儲存在 ResourceTable物件中,因此 AAPT需要遍歷每一個package中的每個tpye,取出每一個 entry。根據這些entry在 type 中的順序計算他們資源ID。那麼就可以生成一個資源符號了。這個資源符號由名稱和資源ID組成。
對於strings.xml檔案中名稱為“start_in_process”的Entry來說,它是一個型別為string的資源項,假設它出現的次序為第3,那麼它的資源符號就等於R.string.start_in_process,對應的資源ID就為0x7f050002,其中,高位元組0x7f表示Package ID,次高位元組0x05表示string的Type ID,而低兩位元組0x02就表示“start_in_process”是第三個出現的字串。
九、生成資源索引表
經過上面八個步驟,終於獲得了資源列表,有了這個 aapt 就可以按照下面的流程生成 資源索引表 resources.arsc。
感覺又是很多步驟,真實心累的一批哈,看老羅的文章真的是想睡啊~~上面我們壓平了xml,基本已經完成了一半的任務了,剩下一般就是生成這個resources.arsc 檔案,估計又是一大堆的規則。知道大概意思就行了,到了需要的時候再仔細研究就好了。
step 1、收集型別字串
一共有四種型別分別是 drawable 、layout、string 和 id。對應的型別字串也是drawable 、layout、string 和 id。注意這些字串是按照報名 package 來收集的。有幾個報名就有幾組對應的型別字串。
step 2、收集資源項 名稱 字串 比如上面的例子中有 12 個資源項,“icon”、“icon”、“icon”、“main”、“sub”、“app_name”、“sub_activity”、“start_in_process”、“start_in_new_process”、“finish”、“button_start_in_process”和“button_start_in_new_process” 對應的名稱字串也是它們。對的這個也按照 package 來分組。
step 3、收集資源項 值 字串 上一步是 名稱 這一回是 值。一共有12個資源項,但是隻有10項是具有值字串的,它們分別是“res/drawable-ldpi/icon.png”、“res/drawable-mdpi/icon.png”、“res/drawable-hdpi/icon.png”、“res/layout/main.xml”、“res/layout/sub.xml”、“Activity”、“Sub Activity”、“Start sub-activity in process”、“Start sub-activity in new process”和“Finish activity”。需要注意的是這些字串不是按照包 package 來區分的,會被統一收集起來。
step 4、生成package資料塊 參與編譯的每一個 package 的資源項 元資訊 都寫在一個獨立的資料上,這個資料塊使用和一個型別為 ResTable_package 的頭部來描述。最後是下圖這樣的形式來的。
說一個比較不注意的東西點,但是感覺挺重要的點吧,在Android資源中,有一種資源型別成為public,他們一般是定義在 res/values/public.xml 。比如下面這樣的,這個public.xml是用來告訴aapt,將型別為string的資源string3的ID 固定為0x7f040001,為什麼要固定呢?當我們自己自定義的資源匯出來給第三方程式使用的時候,為了保證以後修改這些匯出資源時,仍然能保證第三方應用程式的相容性,就需要給這些匯出資源一個固定的資源ID。舉個例子,不然不好理解。那就對比一下有什麼區別吧,我們首先建立一個public.xml 裡面放一個string3.然後新建兩個String,一個string3,一個string1。我們看一下public和沒有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>
<string name="string1">String 1</string>
<string name="string3">String 3</string>
</resources>
複製程式碼
假設 資源ID是下圖這樣的,那麼第三方引用string3的資源ID永遠是0x7f040001。
public final class R {
// ...
public static final class string {
public static final int string1=0x7f040000;
public static final int string3=0x7f040001;
}
}
複製程式碼
當有一天,我們增加一個string,按照我們之前所說的他們是按順序來收集以及分配資源ID的。所以會有所改變。
<?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>
複製程式碼
假設string3 沒有引入public.xml 中,我們應該猜到是下圖這樣的
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中了,所以它的資源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
}
}
複製程式碼
需要注意的是我們自己開發應用程式是不需要pubic.xml 這麼搞一下的,基本都是內部使用。不會匯出來給第三方app使用,只在內部使用的資源,不管它的資源ID怎麼變化,我們都能通過R.java 檔案定義的常量來引用他們。只有系統定義的資源包才會使用到public.xml 檔案。因為它定義的資源需要提供給第三方應用程式使用的。
其實有一個更常見的情景,我們在反編譯的時候,就會看到有這個public.xml 發現R.java的id好像都跑到public.xml裡面去了,這是為什麼呢?因為我們反編譯之後再重新打包,編譯,對資源ID會重新編排這是一個隨機的過程,但是我們的程式碼裡面還是之前的資源id,那麼就亂套了,所以生成一個public.xml保持這些資源ID的固定,感覺又學到知識了,啊哈哈,以前沒怎麼關心過這個東西,不知道這個還有這一層含義。
繼續我們的 生成package資料塊
- 寫入Package資源項元資訊資料塊頭部
- 寫入型別字串資源池 在上面步驟中我們將每一個package 用到的型別字串手機起來了,因此直接把他寫到package資源項元資訊塊頭部後面的資料塊中去。
3.寫入資源名稱字串資源池 我們已經把資源項名稱字串收集了。因此可以將他們直接寫到型別字串資源池後面的那個資料塊中。 4.寫入型別規範資料塊 每一個型別都對應了一個型別規範資料塊 5.寫入型別資源項資料塊
step 5、寫入資源索引表頭部 step 6、寫入資源項的值字串資源池 在前三個步驟中已經收集了這些,我們按照相關規則寫入就可以了。 step 7、寫入package資料塊 在第四步的時候我們已經收集到了它的資訊,同樣按規則寫入就可以了。
十、編譯AndroidManifest檔案 經過前面九個步驟,終於把應用程式的所有資源項都編譯完成了。這個時候就開始講應用程式的配置檔案 AndroidManifest.xml也編譯成二進位制的xml檔案。和之前的道理是一樣的。當然aapt也會驗證這個檔案的完整性和正確性什麼的。
十一、生成R.java 在第八步的時候,我們已經收集到這些資源ID,這裡只是寫入到R.java 檔案中就好了。
public final class R {
......
public static final class layout {
public static final int main=0x7f030000;
public static final int sub=0x7f030001;
}
......
}
複製程式碼
十二、打包APK檔案 所有資原始檔都編譯以及生成完畢之後就可以打包到apk檔案中去了。包括以下檔案: 1、assert目錄 2、res目錄,但是不包括 res/values 目錄,這是因為 res/values 目錄下的檔案經過編譯後,直接寫入到了資源項索引表去了。 3、資源項索引檔案 resources.arsc 當然除了這些資原始檔,應用的配置檔案 AndroidManifest.xml 以及應用程式碼檔案 class.dex,還有用來描述程式的簽名資訊的檔案,也會被一併打包到 APK中去,這個APK檔案可以直接安裝了。
終於看完了,其中最重要的四個要點:
1、xml 資原始檔從文字格式編譯成二進位制格式的過程
2、xml 資原始檔的二進位制格式
3、專案資源索引檔案 resources.arsc 的生成過程
4、專案資源索引檔案 resources.arsc的二進位制格式
www.jianshu.com/p/d487f0aa9… blog.csdn.net/luoshengyan… juejin.im/entry/58b78… www.10tiao.com/html/597/20… www.jianshu.com/p/3cc131db2…