Android打包apk原理與流程

Android Dex分包 

 

一、Ant原始命令行打包流程。

首先總結一下apk打包的一個大致流程,方便後邊大家理解。

大致分爲三步,每一步中又包含了一些詳細的操作步驟:

  1. 打包生成.dex文件。
  2. 打包生成resouse.zip文件。
  3. 生成.apk文件並簽名。

 

1.打包生成.dex文件。

我們如果想要生成.dex文件,必須通過工具將一些資源與java代碼生成.class文件,其中包含一些資源文件、所有java代碼文件、aidl文件等。首先需要在環境變量中配置好我們將要用到的工具,包括JDK、SDK、ANT,配置方法在這裏就不多說了,網上一查一大片。然後我們先要用到SDK包下的一個aapt工具,這個工具後面還會被用到一次,工具在/SDK/build-tools/目錄下,我們先用它生成應用的R.java文件。先cd到當前目錄下然後輸入aapt工具相關命令行及相關參數,如下:

aapt package -f -m -J C:\Users\jason\Desktop\ -S D:\lylsoft\android\androidstudio\opensource\MyButterKnifeDemo\app\src\main\res -M D:\lylsoft\android\androidstudio\opensource\MyButterKnifeDemo\app\src\main\AndroidManifest.xml -I D:\lylsoft\android\androidstudio\sdk\platforms\android-24\android.jar


參數含義如下:
-f 如果編譯生成的文件已經存在,強制覆蓋。

-m 使生成的包的目錄存放在-J參數指定的目錄

-J 指定生成的R.java 的輸出目錄路徑(存放在桌面的gen)

-S 指定目標項目res文件夾的路徑

-M 指定目標項目AndroidManifest.xml文件的路徑

-I 指定某個版本平臺的android.jar文件的路徑

回車執行,便會在我的桌面上生成如下目錄結構及R.java文件

1.1、第一小步,瞭解一下它的實現過程:

aapt工具的源碼在android系統源碼的frameworks\base\tools\aapt目錄下,生成的過程主要是調用了aapt源碼目錄下Resource.cpp文件中的buildResources()函數,該函數首先檢查AndroidManifest.xml的合法性,然後對res目錄下的資源子目錄進行處理,處理的函數爲makeFileResources(),處理的內容包括資源文件名的合法性,向資源表table添加條目等,處理完後調用complieResourceFlie()函數編譯res與asserts目錄下的資源並生成resources.arsc文件,compileResourceFile()函數位於aapt源碼目錄的ResourceTable.cpp文件中,該函數最後會調用parseAndAddEntry()函數生成R.java文件,完成資源編譯後,接下來調用compileXmlFile()函數對res目錄的子目錄下的xml文件分別進行編譯,這樣處理過得xml文件就簡單的被“加密”過了,最後將所有的資源與編譯生成的resources.arsc文件以及“加密”過的AndroidManifest.xml文件打包壓縮成resources.ap_文件(使用ant工具命令行編譯則會生成與buiild.xml中“project name”指定的屬性同名的ap_文件)。資源文件中res/animator、res/anim、res/color、res/drawable(非Bitmap文件,即非.png、.9.png、.jpg、.gif文件)、res/layout、res/menu、res/values和res/xml的資源文件均會從文本格式的XML文件編譯成二進制格式的XML文件,assets和res/raw原封不動打包進去。除了assets資源之外,其它的資源都會被賦予一個資源ID。

1.2、第二小步是處理aidl文件:

aidl -ID:/JavaTest/Demos/src D:/JavaTest/Demos/src/com/example/android/apis/app/IRemoteService.aidl
"-I"與"D:/JavaTest/***"之間是沒有空格的。執行此條命令後,生成的.java會與.aidl文件在同一目錄下。對於沒有使用到aidl的Android工程,這一步可以跳過。這一步使用到的工具爲aidl,位於android-sdk\platform-tools目錄下,aidl工具解析接口定義文件(aidl爲android interface definition language的首字母縮寫,即android接口描述語言)並生成相應的java代碼供程序調用,源碼位於Android源碼的frameworks\base\tools\aidl目錄下。


1.3、第三小步通過javac命令將所有的.java文件編譯成.class文件包括上邊生成的R.java文件:


javac -target 1.8 -bootclasspath D:\lylsoft\android\androidstudio\sdk\platforms\android-24\android.jar -d C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo -sourcepath D:\lylsoft\android\androidstudio\opensource\MyButterKnifeDemo\app\src C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\R.java
執行完上述命令後在桌面上的文件夾中就會出現如下目錄

參數含義如下:
-target <jdk版本>            生成特定 jdk 版本的類文件
-bootclasspath <路徑>   覆蓋引導類文件的位值
-d <目錄>                       指定存放生成的類文件的位置
-sourcepath <路徑>       指定查找輸入源文件的位置(這裏包含兩個地方一個是src,一個是桌面gen生成的R.java)

 

1.4、第四小步就是生成.dex文件:

這個相對簡單,主要用到SDK/build-tools中的dx工具。命令如下:

dx --dex --output=\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\classes.dex C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\com\demo\jason\mybutterknifedemo

--output=<要生成的classes.dex路徑>  <要處理的class文件的路徑>
這樣便會生成我們的.dex文件了,這裏如果可能會報一個RuntimeException----ParseException這是由於我們引入的jar包編譯環境(比如是1.8)要高於android中默認的jdk編譯版本(我用的是1.7)。要解決這個問題,只要android項目的編譯jdk版本要高於等於引入jar包的編譯jdk版本,然後重新編譯就好了。既然如此修改eclipse的jdk編譯版本改爲1.8就好了。
這樣我們的第一步生成.dex文件就算是完成了

二.打包生成Resource.zip文件。

2.1、在這裏就第二次用到了我們sdk中的aapt工具了,命令如下:

aapt package -f -M AndroidManifest.xml -S D:\lylsoft\android\androidstudio\opensource\MyButterKnifeDemo\app\src\main\res -I D:\lylsoft\android\androidstudio\sdk\platforms\android-24\android.jar -A D:\lylsoft\android\androidstudio\opensource\MyButterKnifeDemo\app\src\main -F C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\Resource.zip


參數介紹如下,跟生成R.java文件差不多:
-f 如果編譯生成的文件已經存在,強制覆蓋。
-m 使生成的包的目錄存放在-J參數指定的目錄
-S 指定res文件夾的路徑
-I 指定某個版本平臺的android.jar文件的路徑
-A 指定assert文件夾的路徑
-F 指定輸出文件完整路徑。

回車執行,我們的Resource.zip文件夾就算是生成了,這個名字是可以指定的。這一步比較簡單,沒有太多要說的。接着往下看。第三步就是生成我們的.apk文件了,這裏的.apk文件是沒有簽名沒有優化的.apk文件。

三.生成.apk文件並簽名優化。

這裏需要組合我們上邊生成的兩大文件,即.dex文件和Resource.zip文件。這裏主要使用apkbuilder腳本,其實是個批處理文件,不過android 3.0後已經被刪除,但網上還是可以找到這個腳本的,直接拷貝放到/SDK/tools下即可。其實裏面執行的就是sdklib.jar。

apkbuilder C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\demo.apk -v -u -z C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\Resource.zip -f C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\classes.dex


參數含義如下:
第一個參數是存放打包後的文件完整路徑

-v Verbose 顯示過程信息

-u 創建一個無簽名的包

-z 指定apk資源路徑

-f 指定dex文件路徑

回車執行:在我們指定的路徑下就會生成我們沒有優化簽名的apk文件,如下圖:

打包的工具爲apkbuilder,它位於android-sdk\tools目錄下,apkbuilder爲一個腳本文件,實際調用的是android-sdk\tools\lib\sdklib.jar文件中的com.android.sdklib.build.apkbuilderMain類。它的實現代碼位於android系統源碼的sdk\sdkmanager\libs\sdklib\src\com\android\sdklib\build\akpbuilderMain.java文件,代碼構建了一個apkbuilder類,然後以包含resources.arec的文件爲基礎生成apk文件,這個文件一般爲ap_結尾的文件,接着調用addSourceFolder()函數添加的資源,addSourceFolder()會調用processFileForResource()函數往apk文件中添加資源,處理的內容包括res目錄與assets目錄中的文件,添加完整源後調用addResourcesFromJar()函數往apk文件中寫入依賴庫,接着調用addNativeLibraries()函數添加工程libs目錄下的Native庫(通過androidDNK編譯生成的so或bin文件),最後條用sealApk()關閉apk文件。

接下來就是給我們的demo.apk進行簽名了,用到了jarsigner命令,java的簽名工具,如下:

jarsigner -verbose -keystore C:\Users\jason\Desktop\demo.keystore -storepass android -keypass android -signedjar C:\Users\jason\Desktop\com\demo\jason\mybutterknifedemo\demo.apk C:\Users\jason\Desktop\com\demo\jason\demo.apk demo1


各個參數的意義如下:

-verbose  簽名/驗證時輸出詳細信息

-keystore  密鑰庫路徑

-storepass  用於密鑰庫完整性的口令(密碼)

-keypass    專用密鑰的口令(密碼)

-signedjar   已簽名的 apk 文件的名稱 (第一個apk是簽名之後的文件, 第二個apk是需要簽名的文件)

回車執行,稍等片刻,在我們指定的文件架下就會生成我們已經打包好的apk文件了,這個已經是簽名好的apk文件了。
下一步就是用zipalign對我們的apk進行優化了,我們可以通過zipalign對apk進行檢測。如下:


zipalign -c -v 4 C:\Users\jason\Desktop\com\demo\jason\demo.apk
-c 表示進行zipalign優化檢測

-v 輸出過程信息

如果我們沒有優化過我們的apk文件,則會認證失敗,會看到Verification FAILED的相關信息。然後我們就可以優化我們的apk文件了。如下:


zipalign -f -v 4 C:\Users\jason\Desktop\com\demo\jason\demo.apk C:\Users\jason\Desktop\com\demo\jason\demo1.apk
-f  如果文件已經存在,強制覆蓋

-v 輸出詳細信息

- 需要zipalign優化的apk  優化後的apk名稱以及存放位置

運行完成後我們的指定文件架下就出現了我們已經優化好的apk文件了。如下圖


我們這次執行我們的檢測命令發現最後驗證成功了(打印出Verification succesful)。

zipalign工具位於android-sdk\tools目錄,源碼位於android系統源碼的build\tools\zipalign,它的主要工作是將apk包進行對齊處理,使apk包中的所有資源文件距離文件起始偏移爲4字節整數倍,這樣通過內存映射訪問Apk文件時的速度會更快,驗證apk文件是否對齊過的工作由ZipAlign.cpp文件的verify()函數完成,處理對齊的工作則由process()函數完成。


好了以上就是我們用純命令行的方式打包一個apk文件的過程,是不是感覺很麻煩,我們一般在開發者沒人這麼搞,寫這麼多,主要是自己屢一下打包邏輯,同時幫助大家總結一下,讓大家都知道整個apk是如何創建成功並進行簽名優化的,其中用到了哪些工具。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章