官方介紹
在分析安裝程序之前,需要先了解一下 Android 專案是如何經過編譯->打包生成最終的 .apk 格式的安裝包,谷歌有一張官方圖片來描述 apk 的打包流程,如下圖所示,

Android 應用模塊的構建程序(如上圖所示)遵循以下一般步驟:
-
1、編譯器將你的源代碼轉換為 DEX(Dalvik 可執行檔案)檔案,其中包括在 Android 設備上運行的位元組碼,以及其他所有內容到編譯資源中,
-
2、APKPackager將DEX檔案和編譯后的資源組合成一個APK,但是,在將你的應用安裝并部署到Android設備之前,必須對APK進行簽名,
- 3、APKPackager使用除錯或發布密鑰庫對你的 APK 進行簽名:
-
3.1如果你正在構建應用程式的除錯版本,即你打算僅用于測驗和分析的應用程式,則打包程式會使用除錯密鑰庫對你的應用程式進行簽名,Android Studio 使用除錯密鑰庫自動配置新專案,
-
3.2如果你正在構建你打算在外部發布的應用程式的發布版本,則打包程式會使用發布密鑰庫對你的應用程式進行簽名,
-
-
4、在生成最終的 APK 之前,打包程式使用zipalign工具來優化你的應用程式,以便在設備上運行時使用更少的記憶體,
在構建程序結束時,你將擁有應用的除錯 APK 或發布 APK,可用于部署、測驗或發布給外部用戶,
以上是官方介紹,下面咱開始自己的理解,
自我救贖
開始新專案時,Android Studio 會自動為您創建其中的部分檔案,并為其填充合理的默認值,所以不管一個完整的 Android 專案可能包含多個 module,而從宏觀上看每一個 module 中的內容可以分為 2 部分:
-
Resources 資源檔案
-
Java 或者 Kotlin 源代碼,
因此整個專案的編譯打包程序也是針對這 2 部分來完成,如下圖:

編譯階段
Resources 資源檔案
資源檔案包括專案中 res 目錄下的各種 XML 檔案、影片、drawable 圖片、音視頻等,AAPT 工具負責編譯專案中的這些資源檔案,所有資源檔案會被編譯處理,XML 檔案(drawable 圖片除外)會被編譯成二進制檔案,所以解壓 apk 之后無法直接打開 XML 檔案,但是 assets 和 raw 目錄下的資源并不會被編譯,會被原封不動的打包到 apk 壓縮包中,
資源檔案編譯之后的產物包括兩部分:resources.arsc 檔案和一個 R 檔案,前者保存的是一個資源索引表,后者定義了各個資源 ID 常量,這兩者結合就可以在代碼中找到對應的資源參考,如 下圖 檔案:

可以看出,R 檔案 中的資源 ID 是一個 4 位元組的無符號整數,用 16 進制表示,其中,最高的 1 位元組表示 Package ID,次高的 1 個位元組表示 Type ID,最低的 2 位元組表示 Entry ID,
resources.arsc 相當于一個資源索引表,也可以理解為一個 map 映射表,其中 map 的 key 就是 R.java 中的資源 ID,而 value 就是其對應的資源所在路徑,實際上 resources.arsc 里面還有其他資訊,關于 resource.arsc 的決議可以參考 決議編譯之后的Resource.arsc檔案格式,
原始碼部分
專案中的源代碼首先會通過 javac 編譯為 .class 位元組碼檔案,然后這些 .class 檔案連同依賴的三方庫中的 .class 檔案一同被 dx 工具優化為 .dex 檔案,如果有分包,那么也可能會生成多個 .dex 檔案,
實際上源代碼檔案也包括 AIDL 介面檔案編譯之后生成的 .java 檔案,Android 專案中如果包含 .aidl 介面檔案,這些 .aidl 檔案會被編譯成 .java 檔案,
打包階段
最后使用工具 APK Builder 將經過編譯之后的 resource 和 .dex 檔案一起打包到 apk 中,實際上被打包到 apk 中的還有一些其他資源,比如 AndroidManifest.xml 清單檔案和三方庫中使用的動態庫 .so 檔案,
APK檔案結構
APK(Android Package),可以看做是一個zip壓縮包,可以將.apk改為.zip解壓,其檔案結構如下:

-
assert:存放的原生資源檔案,通過AssetManager類訪問
-
lib:native庫檔案
- META-INF:存放簽名資訊,用來保證APK包的完整性和系統的安全,系統安裝APK時,應用管理器會按照對應演算法對包里檔案做校驗,如果校驗結果與META-INF中內容不一致,則不會安裝這個APK,
-
CERT.SF:生成每個檔案相對的密鑰
-
MANIFEST.MF:數字簽名資訊
-
-
res:種資源檔案系統會在R.java里面自動生成該資源檔案的ID,所以訪問這種資源檔案比較簡單,通過R.XXX.ID即可
-
AndroidManifest.xml:每個應用都必須定義和包含,描述應用的名字、版本權限、參考的庫檔案等資訊,apk中的AndroidManifest.xml經過壓縮,可以通過AXMLPrinter2工具解開,
-
classes.dex:是JAVA原始碼編譯后生成的JAVA位元組碼檔案,但Android使用的dalvik虛擬機與標準的JAVA虛擬機不兼容,dex檔案與class檔案相比,不論是檔案結構還是opcode都不一樣,
-
resources.arsc:編譯后的二進制資源檔案, apk 創建好之后,還不能直接使用,需要使用工具 jarsigner 對其進行簽名,因為 Android 系統不會安裝沒有進行簽名的程式,簽名之后會生成 META_INF 檔案夾,此檔案夾中保存著跟簽名相關的各個檔案,
PackageManagerService(PMS) 在安裝程序中會檢查 apk 中的簽名證書的合法性,具體內容稍后介紹,
常理來說,簽名之后的 apk 應該是可以正常安裝使用了,但是實際打包程序還會多一步使用工具 zipalign 對 apk 優化操作,
zipalign:是一種 zip 歸檔檔案對齊工具,它對 apk 中的未壓縮資源(圖片、視頻等)進行對齊操作,相對于檔案開頭都是對齊的,這樣一來,你便可直接通過 mmap(2) 訪問這些檔案,而無需在 RAM 中復制相關資料并減少了應用的記憶體用量,
在將 APK 檔案分發給最終用戶之前,應該先使用 zipalign 進行優化,如果你使用 Android Studio 進行構建,則此步驟會自動完成,
mmap(2):mmap, munmap - 將檔案或設備映射或取消映射到記憶體中,
至此一個完整的 apk 安裝包就創建成功,
整個編譯打包流程可以用下圖來描述:

AIDL:AIDL 是 Android 中 IPC(Inter-Process Communication,行程間通信)方式中的一種,AIDL是Android Interface definition language的縮寫,對于小白來說,AIDL的作用是讓讓你可以在自己的 APP 里系結一個其他 APP 的Service,這樣你的 APP 可以和其他 APP 互動,
注意:只有在需要不同應用的客戶端通過 IPC 方式訪問服務,并且希望在服務中進行多執行緒處理時,你才有必要使用 AIDL,如果你無需跨不同應用執行并發 IPC,則應通過實作 Binder 來創建介面;或者,如果你想執行 IPC,但不需要處理多執行緒,請使用 Messenger 來實作介面, 無論如何,在實作 AIDL 之前,請你務必理解系結服務(系結服務是 Service 類的實作,可讓其他應用與其進行系結和互動,),
AAPT2:會決議資源、為資源編制索引,并將資源編譯為針對 Android 平臺進行過優化的二進制格式,
javac:將所有 .java 檔案 (包括 R 檔案和 aidl 生成的 .java 檔案),通過 javac 工具生成 .class 檔案,
dx: .class 檔案連同依賴的三方庫中的 .class 檔案一同被 dx 工具優化為 .dex 檔案,
Apk 已經打包好了,接下來看一下 PackageManagerService 是如何將其安裝到設備中的這個下文給到,
相關推薦
Android Gradle 詳解
Android apk 包體積優化
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/301078.html
標籤:其他
