在Android Studio中直接點擊 Run ‘app’ 就能夠在build/outputs/apk生成能夠在android設備中安裝的Apk文件,那麼Apk生成的過程是怎麼樣的呢?
html
Apk文件大概能夠分爲兩個部分:代碼和資源,因此打包的也分爲代碼和資源兩個部分,咱們能夠根據Google提供的流程圖來具體瞭解一個Apk的構建過程java
新版構建流程圖android
APK打包的內容主要有:應用模塊也就是本身開發的用到的源代碼、資源文件、aidl接口文件,還有就是依賴模塊即源代碼用到的第三方依賴庫如:aar、jar、so文件
數組
爲了可以清楚的瞭解Apk是如何生成的, 來看一下老版構建流程圖安全
老版構建流程圖 bash
在瞭解Apk生成的過程以前,咱們須要瞭解一下圖中各個工具的做用app
名字 | 功能 |
---|---|
AAPT/APT2 | Android資源打包工具 |
AIDL | 將全部的AIDL接口轉化爲java接口 |
Javac(Java Compiler) | 將全部的Java代碼編譯成Class文件 |
Dex | 將Class文件編譯成Dex文件 |
Apkbuilder | 將處理後的資源和代碼打包生成Apk文件 |
Jarsigner/Apksigner | 對未簽名的apk文件進行簽名 |
Zipalign | 優化簽名後的Apk,減小運行時所佔用的內存 |
AAPT(Android Asset Packaging Tool)android資源打包工具,將資源文件(包括AndroidManifest.xml、佈局文件、各類xml資源等)打包生成R.java文件,將AndroidManifest.xml生成二進制的AndroidManifest.java文件
工具
aapt p -M AndroidManifest.xml -S output/res/ -I android.jar -J ./ -F input/out.apk
p:打包
-M:AndroidManifest.xml文件路徑
-S:res目錄路徑
-A:assets目錄路徑
-I:android.jar路徑,會用到的一些系統庫
-J 指定生成的R.java的輸出目錄
-F 具體指定apk文件的輸出
複製代碼
可是從從Android Studio 3.0開始,google默認開啓了AAPT2做爲資源編譯的編譯器,AAPT2的出現,爲資源的增量編譯提供了支持,aapt2 主要分兩步,compile 和 link
佈局
compile優化
aapt2 compile -o res.apk --dir output/res/
-o:指定已編譯資源的輸出路徑
--dir:指定包含多個資源文件的資源目錄
複製代碼
link
aapt2 link -o input/out.apk -I tools/android.jar --manifest output/AndroidManifest.xml -A res.apk --java ./
-o:指定連接的資源 APK 的輸出路徑
-I:指定android.jar路徑
--manifest:指定AndroidManifest.xml路徑
--java :指定要在其中生成 R.java 的目錄
複製代碼
使用AIDL(Android Interface Denifition Language),位於sdk\build-tools目錄下的aidl工具,將源碼文件、aidl文件、framework.aidl等全部的AIDL文件,生成相應的Java文件,命令以下:
aidl -Iaidl -pAndroid/Sdk/platforms/android-29/framework.aidl -obuild aidl/com/android/vending/billing/IInAppBillingService.aidl
-I 指定import語句的搜索路徑,注意-I與目錄之間必定不要有空格
-p 指定系統類的import語句路徑,若是是要用到android.os.Bundle系統的類,必定要設置sdk的framework.aidl 路徑
-o 生成java文件的目錄,注意-o與目錄之間必定不要有空格,並且這設置項必定要在aidl文件路徑以前設置
複製代碼
使用Javac(Java Compiler)把項目中全部的Java代碼編譯成class文件, 包括Java源文件、AAPT生成的R.java文件 以及 aidl生成的Java接口文件,命令以下:
javac -target 1.8 -bootclasspath platforms/android-28/android.jar -d ./java/com/testjni/*.java
複製代碼
使用DX工具將全部的Class文件(包括第三方庫中的class文件)轉換成Dex文件(Dalvik 可執行文件,其中包括在 Android 設備上運行的字節碼),該過程主要完成Java字節碼轉換成Dalvik字節碼, 命令以下:
java -jar dx.jar --dex --ouput=classes.dex ./java/com/testjni/*.class
--dex:將class文件轉成dex文件
--output:指定生成dex文件到具體位置
複製代碼
使用Apkbuilder(主要用到的是sdk/tools/lib/sdklib.jar文件中的ApkBuilderMain類)將全部的Dex文件、Resource.arsc、Res文件夾、Assets文件夾、AndroidManifest.xml 打包生成Apk文件(未簽名)
使用Apksigner(Android官方針對apk簽名及驗證工具)或 Jarsigner(JDK提供針對jar包簽名工具)對未簽名的apk文件進行簽名
ps:若是使用Apksigner簽名須要(7. 優化Apk文件)放到(6. 對Apk文件簽名)簽名前面,爲何?請查看關於Apksigner 和 Jarsigner的區別,請移步到文末
使用zipalign對簽名後的apk文件進行對齊處理,對齊的主要過程是將APK包中全部的資源文件距離文件起始偏移爲4字節整數倍,這樣經過內存映射訪問apk文件時的速度會更快,減小其在設備上運行時所佔用的內存
上述打包過程都是AndroidStudio編譯時,調用各類編譯命令自動完成的, 總結一下上述打包過程:
Apk文件大概能夠分爲兩個部分:代碼和資源, 代碼部分經過Javac將Java代碼編譯成Class文件, 而後經過DX工具將Class文件編譯成Dex文件,接下來咱們主要來分析一下資源的編譯和打包
在分析資源的編譯和打包以前,咱們須要瞭解一下Android都有哪些資源,其實Android資源大概分爲兩個部分:assets 和 res
assets資源放在assets目錄下,它裏面保存一些原始的文件,能夠以任何方式來進行組織,這些文件最終會原封不動的被打包進APK文件中,經過AssetManager來獲取asset資源,代碼以下
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("fileName");
複製代碼
res資源放在主工程的res目錄下,這類資源通常都會在編譯階段生成一個資源ID供咱們使用,res目錄包括animator、anim、 color、drawable、layout、menu、raw、values、xml等
上述資源文件除了raw類型資源,以及drawable文件夾下的Bitmap資源以外,其它的資源文件均會被編譯成二進制格式的XML文件,生成的二進制格式的XML文件分別有一個字符串資源池,用來保存文件中引用到的每個字符串
這樣原來在文本格式的XML文件中的每個放置字符串的地方在二進制格式的XML文件中都被替換成一個索引到字符串資源池的整數值,將整數值保存在R.java類中,R.java會和其餘源文件一塊兒編譯到APK中去
將資源編譯成二進制文件,都是由AAPT工具來完成的,資源打包主要有如下幾個流程:
AAP工具會全部的資源都會生成一個R.java文件,而且每一個資源都對應R.java中的十六進制整數變量,其實這些十六進制的整數是由三部分組成:PackageId + TypeId + ItemValue,代碼所示:
public final class R {
public static final class anim {
public static final int abc_fade_in=0x7f010000;
public static final int abc_fade_in=0x7f010001;
//***
}
public static final class string {
public static final int a11y_no_data=0x7f100000;
public static final int a11y_no_permission=0x7f100001;
//***
}
}
複製代碼
最高字節是Package ID表示命名空間,標明資源的來源,Android系統本身定義了兩個Package ID,系統資源命名空間:0x01 和 應用資源命名空間:0x7f
正由於應用資源命名空間:0x7f,咱們在作插件化的時候就會出現一個問題,宿主和插件包,合併資源後資源id衝突。經過上面分析要解決這個問題,就要爲不一樣的插件設置不一樣的PackageId,而宿主能夠保留原來0x7f不變,這樣就永遠不會有衝突發生了
最終生成的是資源索引表resources.arsc,resources.arsc是一個編譯後的二進制文件, 在AndroidStudio打開resources.arsc文件,以下所示
Android正是利用這個索引表根據資源ID進行資源的查找,爲不一樣語言、不一樣地區、不一樣設備提供相對應的最佳資源。查找和經過Resources和 AssetManger來完成的
在文中提到了兩個工具Apksigner 和 Jarsigner,下面一塊兒來了解一下Apksigner 和 Jarsigner的區別
在Android Studio中點擊菜單 Build->Generate signed apk... 打包簽名過程當中,能夠看到兩種簽名選項 V1(Jar Signature) V2(Full APK Signature)
從Android 7.0開始, 谷歌增長新簽名方案 V2 Scheme (APK Signature),但Android 7.0如下版本, 只能用舊簽名方案 V1 scheme (JAR signing)
來自JDK(Jarsigner),對zip壓縮包的每一個文件進行驗證, 簽名後還能對壓縮包修改(移動/從新壓縮文件),對V1簽名的apk/jar解壓,在META-INF存放簽名文件(MANIFEST.MF, CERT.SF, CERT.RSA), 其中MANIFEST.MF文件保存全部文件的SHA1指紋(除了META-INF文件), 由此可知: V1簽名是對壓縮包中單個文件簽名驗證
來自Google(apksigner), 對zip壓縮包的整個文件驗證, 簽名後不能修改壓縮包(包括zipalign), 對V2簽名的apk解壓, 沒有發現簽名文件, 從新壓縮後V2簽名就失效, 由此可知: V2簽名是對整個APK簽名驗證
建立發佈密鑰庫,請參閱在 Android Studio 中爲應用簽名
注意: apksigner工具默認同時使用V1和V2簽名,以兼容Android 7.0如下版本