Android構建過程——資源合併打包

SamanLan發表於2017-12-13

Android構建會經歷資源合併打包、原始碼編譯、dex生成及打包簽名等步驟。 本文對資源合併打包進行一下小的總結

資源合併

資源合併.png
上面一圖總結了在構建過程中的資源合併情況。

  1. 資源合併包括本地與第三方庫的assets目錄res目錄Androidmanifest.xml
  2. 對於assets目錄res目錄res/values資料夾除外)的資源合併一旦發生衝突,會優先使用本地資源。
  3. 對於res/values資料夾,會進行內容上的合併,後合併的會覆蓋有衝突的前面資源,而且合併先後順序無法確定。
  4. 對於Androidmanifest.xml,我們的apk最終只會包含一個AndroidManifest.xml,但是因為我們的main source setbuild variants和引入的第三方依賴都可能存在Manifest檔案,這時候就需要做合併。如果一個低優先順序的xml結點不能匹配任何高優先順序的結點就會被加入到高優先順序的Manifest檔案裡,如果匹配上了則會進行合併,如果該結點下的存在相同屬性在不同檔案裡具有不同的值時則會報錯需要在較高優先順序的manifest檔案裡新增合併規則標識。(Androidmanifest.xml的優先順序:buildType 設定 > productFlavor 設定 > src/main > dependency&library
  5. 合併規則標識
  • merge:預設合併操作。
  • remove:移除指定的低優先順序的屬性
  • remove-All:移除相同節點型別下所有低優先順序的屬性
  • replace:高優先順序替換低優先順序Manifest檔案中的屬性
  1. 另外,manifest在對檔案進行合併後,還會根據build.gradle的設定覆蓋相關屬性。

舉個例子,下面程式碼進行合併,最後的 label 是 app_name;allowBackup 是 true。

<!--src/main的Androidmanifest.xml-->
<application
        android:name="MyApplication"
        android:icon="@drawable/ic_launcher"
        android:allowBackup="true"
        android:label="@string/app_name"
        <!-- 合併規則標識 -->
        tools:replace="android:allowBackup,android:label">
</application>
<!--dependency&library的Androidmanifest.xml-->
<application
        android:name="MyApplication"
        android:icon="@drawable/ic_launcher"
        android:allowBackup="false"
        android:label="@string/app_name222">
</application>
複製程式碼

下面程式碼向Manifest檔案注入build變數值

<!--Gradle檔案-->
android {
    productFlavors {
        free {
            <!--manifestPlaceholders 相當於佔位符的意思,會替換覆蓋Manifest檔案對應的屬性-->
            manifestPlaceholders = [ activityLabel:"freeName"]
        }
        pro {
            manifestPlaceholders = [ activityLabel:"proName" ]
        }
   }
}

<!--Manifest檔案-->
<activity android:name=".MainActivity" android:label="${activityLabel}" >
複製程式碼

AAPT打包

通過AAPTAndroid Asset Packaging Tool)處理後,會輸出2個檔案:

  • 一個為為app.ap,實際上為一個壓縮包,包含了assetsresAndroidmanifest.xmlresources.arsc
  • 一個R.java,為專案各資源分配了不同的id,id為4位元組無符號(8位)整數,最高位元組表示package id,次高位元組表示type id,後2位元組表示資源在當前型別中出現的序號,如R.string.appname=0x7f07006b中的0x7f代表當前正在編譯的資源包,0x07代表string型別,0x006b代表app_name在string型別中出現的序號。(這裡package id為外掛化的資源合併提供了可操作的地方)

assets是不需要做任何處理的,res/raw只需分配id後與assets一起直接打包到應用程式中;基於下述原因,其它xml檔案則會被編譯成二進位制。

  • 編譯過程中,會把xml中的所有的字串進行收集去重,形成字串資源池,元素中用到字串的地方將被替換成相應的索引,進一步減少檔案大小。(字串變成了整數值的索引,相同字串引用同一個索引)
  • 二進位制格式的xml把標籤屬性/値轉換為相應的索引後,避免了字串解析,從而提高了解析速度。

字串被替換成相應的索引

資源索引表resources.arsc記錄了從資源id到檔案路徑的轉換關係,當應用通過Resources類獲取res檔案資源時,會先從resources.arsc中拿到檔案路徑,然後通過AssetManager進行訪問。

  • Android資源管理框架實際就是由AssetManager和Resources兩個類來實現的。
  • Resources類可以根據ID來查詢資源。
  • AssetManager類根據檔名來查詢資源。

資源尋找過程

resources.arsc

再補上一張官方圖總結總結應用程式資源的編譯、打包以及查詢過程

Paste_Image.png

參考文章:

  1. Android構建過程分析
  2. Android應用程式資源的查詢過程分析
  3. Android應用程式資源的編譯和打包過程分析

相關文章