Android APK 打包過程 MD

Markdown版本筆記 個人GitHub首頁 個人博客 個人微信 個人郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

Android APK 打包流程 MDjava


目錄

APK 的打包流程

參考android

Android的包文件APK分爲兩個部分:代碼和資源,因此打包方面也分爲資源打包和代碼打包兩個方面,這篇文章就來分析資源和代碼的編譯打包原理。git

Android打包流程詳圖:github

總體流程

APK總體的的打包流程以下圖所示:數組

具體說來:微信

  • 經過AAPT工具進行資源文件(包括AndroidManifest.xml、佈局文件、各類xml資源等)的打包,生成R.java文件。
  • 經過AIDL工具處理AIDL文件,生成相應的Java文件。
  • 經過Javac工具編譯項目源碼,生成Class文件。
  • 經過DX工具將全部的Class文件轉換成DEX文件,該過程主要完成Java字節碼轉換成Dalvik字節碼,壓縮常量池以及清除冗餘信息等工做。
  • 經過ApkBuilder工具將資源文件、DEX文件打包生成APK文件。
  • 利用KeyStore對生成的APK文件進行簽名。
  • 若是是正式版的APK,還會利用ZipAlign工具進行對齊處理,對齊的過程就是將APK文件中全部的資源文件舉例文件的起始距離都偏移4字節的整數倍,這樣經過內存映射訪問APK文件的速度會更快。

上述流程都是Android Studio在編譯時調用各類編譯命令自動完成的,具體說來,以下所示:數據結構

一、建立Android工程。app

android create project \
-n packageTest2 \
-a MainActivity \
-k com.package.test2 \
-t android-23 \
-p ./PackageTest2ide

二、編譯R文件函數

aapt package \
-f \
-J ./gen \
-M ./AndroidManifest.xml \
-S ./res/ \
-I /Users/RadAsm/Library/AndroidSDK/sdk/platforms/android-23/android.jar

三、編譯源代碼文件

javac -source 1.6 \
-target 1.6 \
-cp /Users/RadAsm/Library/AndroidSDK/sdk/platforms/android-23/android.jar \
./src/com/packtest/test1/MainActivity.java ./src/com/packtest/test1/R.java \
-d ./gen/classes

四、編譯DEX文件

dx --dex \
--verbose \
--output ./gen/dex/packtest1.dex
./gen/classes/

五、生成APK文件

aapt package
-f \
-J ./gen \
-M ./AndroidManifest.xml \
-S ./res/ \
-I /Users/RadAsm/Library/AndroidSDK/sdk/platforms/android-23/android.jar \
-F ./output/res.apk

六、APK文件對齊

zipalign -v -p 4 packagetest_unsigned.apk packagetest_aligned_unsigned.apk

七、APK簽名

apksigner sign --ks my-release-key.jks my-app.apk

以上即是APK打包的整個流程,咱們再來總結一下:

  • 除了assets和res/raw資源被原裝不動地打包進APK以外,其它的資源都會被編譯或者處理;
  • 除了assets資源以外,其它的資源都會被賦予一個資源ID;
  • 打包工具負責編譯和打包資源,編譯完成以後,會生成一個resources.arsc文件和一個R.java,前者保存的是一個資源索引表,後者定義了各個資源ID常量。
  • 應用程序配置文件AndroidManifest.xml一樣會被編譯成二進制的XML文件,而後再打包到APK裏面去。
  • 應用程序在運行時經過AssetManager來訪問資源,或經過資源ID來訪問,或經過文件名來訪問。

理解了總體的流程,咱們再來看看具體的細節。

資源的編譯和打包

在分析資源的編譯和打包以前,咱們先來了解一下Android程序包裏有哪些資源。

咱們知道Android應用程序的設計也是代碼與資源相分離的,Android的資源文件能夠分爲兩大類:

assets:assets資源放在主工程assets目錄下,它裏面保存一些原始的文件,能夠以任何方式來進行組織,這些文件最終會原封不動的被打包進APK文件中。

獲取asset資源也十分簡單,以下所示:

InputStream is = getAssets.open("fileName");

res:res資源放在主工程的res目錄下,這類資源通常都會在編譯階段生成一個資源ID供咱們使用。

res資源包含了咱們開發中使用的各類資源,具體說來:

  • animator
  • anim
  • color
  • drawable
  • layout
  • menu
  • raw
  • values
  • xml

這些資源的含義你們應該都很熟悉,這裏就再也不贅述。

上述9種類型的資源文件,除了raw類型資源,以及Bitmap文件的drawable類型資源以外,其它的資源文件均爲文本格式的XML文件,它們在打包的過程當中,會被編譯成二進制格式的XML文件。這些二進制格式的XML文件分別有一個字符串資源池,用來保存文件中引用到的每個字符串,包括XML元素標籤、屬性名稱、屬性值,以及其它的一切文本值所使用到的字符串。這樣原來在文本格式的XML文件中的每個放置字符串的地方在二進制格式的XML文件中都被替換成一個索引到字符串資源池的整數值,這寫整數值統一保存在
R.java類中,R.java會和其餘源文件一塊兒編譯到APK中去。

前面咱們提到xml編寫的Android資源文件都會編譯成二進制格式的xml文件,資源的打包都是由AAPT工具來完成的,資源打包主要有如下流程:

  • 解析AndroidManifest.xml,得到應用程序的包名稱,建立資源表。
  • 添加被引用資源包,被添加的資源會以一種資源ID的方式定義在R.java中。
  • 資源打包工具建立一個AaptAssets對象,收集當前須要編譯的資源文件,收集到的資源保存在AaptAssets對象對象中。
  • 將上一步AaptAssets對象保存的資源,添加到資源表ResourceTable中去,用於最終生成資源描述文件resources.arsc
  • 編譯values類資源,這類資源包括數組、顏色、尺寸、字符串等值。
  • 給bag、style、array這類資源分配資源ID。
  • 編譯xml資源文件,編譯的流程分爲四步:① 解析xml文件 ② 賦予屬性名稱資源ID ③ 解析屬性值 ④ 將xml文件從文本格式轉換爲二進制格式。
  • 生成資源索引表resources.arsc

資源ID

每一個Android項目裏都有有一個R.java文件,以下所示:

public final class R {
     //...
     public static final class anim {
        public static final int abc_fade_in=0x7f010000;
     }
     public static final class attr {
         public static final int actionBarDivider=0x7f020000;
     }
     public static final class string {
          public static final int actionBarDivider=0x7f020000;
     }
     //...
}

每一個資源項後的整數就是資源ID,資源ID是一個4字節的無符整數,以下所示:

  • 最高字節是Package ID表示命名空間,標明資源的來源,Android系統本身定義了兩個Package ID,系統資源命名空間:0x01 和 應用資源命名空間:0x7f。
  • 次字節是Type ID,表示資源的類型,例如:anim、color、string等。
  • 最低兩個字節是Entry ID,表示資源在其所屬資源類型中所出現的次序。

資源索引

上面提到,最終生成的是資源索引表resources.arsc,Android正是利用這個索引表根據資源ID進行資源的查找,爲不一樣語言、不一樣地區、不一樣設備提供相對應的最佳資源。查找是經過Resources和AssetManger來完成的,這個咱們下面會講。

resources.arsc 是一個編譯後的二進制文件,在Android Stduio裏打開之後是這樣的,以下所示:

能夠看到resources.arsc裏存放了各種資源的索引參數和配置信息。

resources.arsc的文件格式以下所示:

注:整個文件都是有一系列chuck(塊)構成的,chuck是整個文件的劃分單位,每一個模塊都是一個chuck,chuck最前面是一個ResChunk_header的結構體,用來描述整個chunk的信息,更多關於索引表格式的細節,能夠查閱源碼:

👉 ResourceTypes.h

resources.arsc 索引表從上至下文件格式依次爲:

  • 文件頭:數據結構用ResTable_header來描述,用來描述整個文件的信息,包括文件頭大小,文件大小,資源包Package的數量等信息。
  • 全局字符串池:存放全部的字符串,因此資源複用這些字符串,字符串裏存放的是資源文件的路徑名和資源值等信息。全局字符串池分爲資源類型(type)字符串池和
  • 資源包:會有多個(例如:系統資源包、應用資源包)。

資源包也被劃分爲如下幾個部分:

  • 包頭:描述資源包相關信息。
  • 資源類型字符串池:存放資源的類型。
  • 資源名稱字符串池:存放資源的名稱。
  • 配置列表:存放語音、位置等手機配置信息,用來做爲查找資源的標準。

從這裏能夠看到resources.arsc索引表存在不少常量池,常量池的使用目的也很明顯,就是提供資源的複用率,減小resources.arsc索引表的體積,提升索引效率。

概況

參考

Android APK是如何來的呢?

懷着這個問題去查資料,發現了下邊這張圖。

解壓一個普通的apk文件後,解壓出來的文件包括:

  • classes.dex:.dex文件
  • resources.arsc:resources resources文件
  • AndroidManifest.xml:AndroidManifest.xml文件
  • res:uncompiled resources
  • META-INF:簽名文件夾
    • MANIFEST.MF文件:版本號以及每個文件的哈希值(BASE64),包括資源文件。這個是對每一個文件的總體進行SHA1(hash)。
    • CERT.SF:這個是對每一個文件的頭3行進行SHA1 hash。
    • CERT.RSA:這個文件保存了簽名和公鑰證書。

具體打包過程

aapt階段

使用aapt來打包res資源文件,生成R.java、resources.arsc和res文件(二進制 & 非二進制如res/raw和pic保持原樣)

  • res目錄,有9種子目錄
  • R.java文件。裏面擁有不少個靜態內部類,好比layout,string等。每當有這種資源添加時,就在R.java文件中添加一條靜態內部類裏的靜態常量類成員,且全部成員都是int類型。
  • resources.arsc文件。這個文件記錄了全部的應用程序資源目錄的信息,包括每個資源名稱、類型、值、ID以及所配置的維度信息。咱們能夠將這個文件想象成是一個資源索引表,這個資源索引表在給定資源ID和設備配置信息的狀況下,可以在應用程序的資源目錄中快速地找到最匹配的資源。

aidl階段

AIDL,Android接口定義語言,Android提供的IPC的一種獨特實現。
這個階段處理.aidl文件,生成對應的Java接口文件。

Java Compiler階段

經過Java Compiler編譯R.java、Java接口文件、Java源文件,生成.class文件。

dex階段

經過dex命令,將.class文件和第三方庫中的.class文件處理生成classes.dex。

apkbuilder階段

classes.dexresources.arscres文件夾(res/raw資源被原裝不動地打包進APK以外,其它的資源都會被編譯或者處理)、Other Resources(assets文件夾)、AndroidManifest.xml打包成apk文件。

Jarsigner階段

對apk進行簽名,能夠進行Debug和Release 簽名。

zipalign階段

release mode 下使用 aipalign 進行align,即對簽名後的apk進行對齊處理。

Zipalign是一個android平臺上整理APK文件的工具,它對apk中未壓縮的數據進行4字節對齊,對齊後就可使用mmap函數讀取文件,能夠像讀取內存同樣對普通文件進行操做。若是沒有4字節對齊,就必須顯式的讀取,這樣比較緩慢而且會耗費額外的內存。

在 Android SDK 中包含一個名爲 zipalign 的工具,它可以對打包後的 app 進行優化。 其位於 SDK 的 \build-tools\23.0.2\zipalign.exe 目錄下

2019-2-18

相關文章
相關標籤/搜索