【朝花夕拾】Android效能優化篇之(四)Apk打包
前言
APK,即Android Package,是將android程式和資源整合在一起,形成的一個.apk檔案。相信所有的Android程式設計師是在IDE的幫助下,完成打包輕而易舉,但對打包流程真正清楚的可能並不多。本章的內容比較簡單,也是非常基礎的內容,但是對理解android應用的結構卻有很大的幫助。筆者寫這篇文章的目的,一方面是為了彌補這方面的盲點,回顧和梳理apk打包方面的理論知識點;第二方面,是為了給後續寫Android虛擬機器知識做鋪墊,進而去研究android的效能優化,這也是把這篇文章放到Android效能優化系列文章當中的原因;第三方面,也是為了方便讀者理解Android虛擬機器的相關內容。
對於在IDE,如Android Studio上操作打包的過程,本文不做演示,對於更深入的原始碼分析,也不在本文討論之列,出於前面說到的原因,本文只簡單闡述其打包流程,本文主要內容如下:
一、apk構建流程圖
以下截圖為Google官方提供的詳細的apk構建過程圖,其中包含了各個環節所用到的工具和中間相關的檔案。
apk構建過程(綠色部分為對應環節工具,藍色部分為相關檔案)
二、構建過程中所用工具
如下截圖展示了apk構建過程中所使用的部分工具,這些工具大部分都在sdk/build-tools/資料夾下:
程式碼混淆所用工具
打包所用工具所在的jar包
三、apk打包流程詳解
依據如上的流程圖和工具圖,下面咱們按照流程順序對其進行講解。
1、aapt打包資源
- 工具:aapt(Android Asset Package Tool Android資源打包工具)
- 工具路徑:sdkpath/build-tools/版本號/aapt.exe和aapt2.exe
- 輸入:Android資原始檔、AndroidManifest.xml
- 輸出: R.java類、二進位制的resource.arsc,res資料夾(包括二進位制的xml、沒被改變的圖片和res/raw檔案)、二進位制的AndroidManifest.xml檔案、沒有改變的assets資料夾。
Android的資原始檔包含了兩類:1)assets類資源。該類資源放在工程目錄的assets根目錄下,存放一些原始檔案,這些檔案不會被編譯為二進位制檔案,而是被原封不動地打包在apk檔案中,同樣也不能通過資源ID來查詢,不儲存在R檔案中。2)res類資源,10種目錄。這類資源儲存在工程目錄中的res目錄下,包含了animator(屬性動畫資源)、anim(補間動畫資源)、color(物件顏色狀態選擇資源)、drawable(xml或Bitmap檔案的影象資源)、layout(佈局檔案資源)、menu(程式選單資源)、mipmap(圖示資源,推薦閱讀: ofollow,noindex">drawable與mipmap的區別 )、raw(不被編譯成二進位制檔案的資源,注意和assets資源的區別,推薦閱讀: assets和raw的區別 )、values(6種不同的值:陣列arrays.xml、顏色值colors.xml、尺寸dimens.xml、字串strings.xml和樣式值styles.xml)、xml(描述應用程式配置資訊的資源)。
如下截圖展示了R.java的內容,其中包含了各種靜態內部類,分別對應了某種資源的型別。
R.java結構圖
以R.string類為例,其中展示了字串名稱對應的id值,就是對應在res/values資料夾下,string字串資源。
R.string結構圖
推薦閱讀: apk打包安裝過程
2、aidl生成跨程序通訊的java檔案
- 工具:aidl(Android Interface Definition Language安卓介面定義語言)
- 工具路徑:sdkpath/build-tools/版本號/aidl.exe
- 輸入:aidl字尾的檔案,位於工程專案src/main/aidl目錄下
- 輸出:可用於程序間通訊的C/S端java程式碼,位於build/generated/source/aidl
工程專案中的aidl原始檔案
aidl工具處理後生成的java檔案
3、Java編譯原始碼
- 工具:javac.exe
- 工具路徑:jdk/bin/javac.exe
- 輸入:java source資料夾、aapt中生成的R.java檔案、aidl生成的java檔案、BuildConfig.java檔案
- 輸出:對於gradle編譯,生成的class檔案儲存在build/intermediates/classes裡
BuildConfig.java和R.java檔案
輸出的class檔案
4、proguard程式碼混淆
完成javac編譯之後,一般還會對其進行程式碼的混淆,其實就是類似於加密的功能,作用就是增加反編譯的難度,同時也將一些程式碼的命名進行了縮短,減少程式碼佔用的空間。推薦閱讀: Android程式碼混淆零基礎入門 。
- 工具:ProGuard
- 工具路徑:sdk/tools/proguard/bin/proguard.bat
- 輸入:被編譯過的class檔案、混淆配置檔案proguard-rules.pro
- 輸出:被混淆過的.class檔案、混淆前後對映檔案
5、將所有.class檔案轉化為classes.dex檔案
- 工具:dx.bat
- 工具路徑:sdkpath/build-tools/版本號/dx.bat
- 輸入:編譯後生成的所有.class檔案、第三方庫和.class檔案
- 輸出:可以在Android虛擬機器上使用的.dex檔案
呼叫dx.bat將所有的class檔案轉化為classes.dex檔案,將二進位制碼轉化為Android虛擬機器(Android4.4以前虛擬機器是Dalvik,4.4上是Dalvik和ART可以切換、Android5.0及以後是ART)上的位元組碼、生成常量池、消除冗餘資料等。由於Android虛擬機器是一種針對嵌入式裝置而特殊設計的java虛擬機器,所有dex檔案與標準的class檔案在結構設計上有著很大的區別,當javac將java程式編譯成class後,dx工具將所有的class檔案整合到一個dex檔案中,這樣做使得各個類能夠共享資料,在一定程度上降低了容易,同時也使文結構更加緊湊,實驗表明,dex檔案時傳統jar檔案的50%左右。class檔案結構和dex檔案結構比對如下(該部分還會在後文講Android虛擬機器時提到):
.class檔案和.dex檔案結構對比圖
6、apkbuilder打包生成apk
- 工具:ApkBuilder類
- 工具路徑:sdkpath/tools/lib/sdklib_xxx.jar
- 輸入:上一步生成的classes.dex檔案,aapt時生成的resources.arsc、被編譯後的res資料夾、AndroidManifest.xml,Other Resouces(assets資料夾)
- 輸出:.apk檔案(Android Package)
7、對apk進行簽名
- 工具:apksigner.bat
- 工具路徑:sdkpath/build-tools/版本號/apksigner.bat
- 輸入:上一步中生成的.apk檔案、簽名檔案(Debug or Release Keystore)
- 輸出:簽名後的.apk檔案
簽名是一個apk身份的證明,Android系統在安裝apk的時候,首先會檢驗apk的簽名,如果發現簽名檔案不存在或者校驗簽名失敗,就會拒絕安裝。對一個apk檔案簽名後,apk檔案根目錄下回增加META-INF目錄,該目錄下有三個檔案:
META-IINF資料夾結構
Android系統就是根據這三個檔案的內容對apk檔案進行簽名驗證的:
MANIFEST.MF中包含對apk中除了/META-INF資料夾外所有檔案的簽名值。
MANIFEST.MF內容截圖
CERT.SF是對MANIFEST.MF檔案整體簽名以及其中各個條目的簽名。一般地,如果是使用工具簽名,還多包括一項,就是對MANIFEST.MF頭部資訊簽名。
CERT.SF內容截圖
CERT.RSA包含用私鑰對CERT.SF的簽名以及包含公鑰資訊的數字證書。用一般的文字開啟後,會顯示亂碼。
CETR.RSA內容截圖
推薦閱讀: Android簽名有什麼用?
8、zipalign優化
如果是在release mode下,還會對apk進行align,即對簽名後的apk進行對齊處理,這種方式是對apk進行整理和優化。
- 工具:zipalign
- 工具路徑:sdkpath/build-tools/版本號/zipalign.exe
- 輸入:上一步中籤名後的apk檔案
- 輸出:優化後的apk檔案
四、APK檔案結構
一個apk解壓後,其典型的結構如下所示,分別在apk打包流程中appt資源打包、javac編譯、簽名階段所產生: