oxA01【Android應用框架】Apk是如何生成的

前言

在Android Studio中直接點擊 Run ‘app’ 就能夠在build/outputs/apk生成能夠在android設備中安裝的Apk文件,那麼Apk生成的過程是怎麼樣的呢?
html

Apk文件大概能夠分爲兩個部分:代碼和資源,因此打包的也分爲代碼和資源兩個部分,咱們能夠根據Google提供的流程圖來具體瞭解一個Apk的構建過程java

新版構建流程圖android

15813472877044-w350

APK打包的內容主要有:應用模塊也就是本身開發的用到的源代碼、資源文件、aidl接口文件,還有就是依賴模塊即源代碼用到的第三方依賴庫如:aar、jar、so文件
數組

爲了可以清楚的瞭解Apk是如何生成的, 來看一下老版構建流程圖安全

老版構建流程圖 bash

2019-03-22-15532697195669-w350

在瞭解Apk生成的過程以前,咱們須要瞭解一下圖中各個工具的做用app

工具

名字 功能
AAPT/APT2 Android資源打包工具
AIDL 將全部的AIDL接口轉化爲java接口
Javac(Java Compiler) 將全部的Java代碼編譯成Class文件
Dex 將Class文件編譯成Dex文件
Apkbuilder 將處理後的資源和代碼打包生成Apk文件
Jarsigner/Apksigner 對未簽名的apk文件進行簽名
Zipalign 優化簽名後的Apk,減小運行時所佔用的內存

構建過程

1. 使用AAPT工具生成R.java文件

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 的目錄
複製代碼

2. 全部的AIDL接口轉化爲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文件路徑以前設置
複製代碼

3. 將Java代碼編譯成Class文件

使用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
複製代碼

4. 將Class文件編譯成Dex文件

使用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文件到具體位置
複製代碼

5. 打包生成Apk文件

使用Apkbuilder(主要用到的是sdk/tools/lib/sdklib.jar文件中的ApkBuilderMain類)將全部的Dex文件、Resource.arsc、Res文件夾、Assets文件夾、AndroidManifest.xml 打包生成Apk文件(未簽名)

6. 對Apk文件簽名

使用Apksigner(Android官方針對apk簽名及驗證工具)或 Jarsigner(JDK提供針對jar包簽名工具)對未簽名的apk文件進行簽名

ps:若是使用Apksigner簽名須要(7. 優化Apk文件)放到(6. 對Apk文件簽名)簽名前面,爲何?請查看關於Apksigner 和 Jarsigner的區別,請移步到文末

7. 優化Apk文件

使用zipalign對簽名後的apk文件進行對齊處理,對齊的主要過程是將APK包中全部的資源文件距離文件起始偏移爲4字節整數倍,這樣經過內存映射訪問apk文件時的速度會更快,減小其在設備上運行時所佔用的內存

總結

上述打包過程都是AndroidStudio編譯時,調用各類編譯命令自動完成的, 總結一下上述打包過程:

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

Apk文件大概能夠分爲兩個部分:代碼和資源, 代碼部分經過Javac將Java代碼編譯成Class文件, 而後經過DX工具將Class文件編譯成Dex文件,接下來咱們主要來分析一下資源的編譯和打包

資源的編譯和打包

在分析資源的編譯和打包以前,咱們須要瞭解一下Android都有哪些資源,其實Android資源大概分爲兩個部分:assets 和 res

1. assets資源

assets資源放在assets目錄下,它裏面保存一些原始的文件,能夠以任何方式來進行組織,這些文件最終會原封不動的被打包進APK文件中,經過AssetManager來獲取asset資源,代碼以下

AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("fileName");
複製代碼

2. res資源

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工具來完成的,資源打包主要有如下幾個流程:

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

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不變,這樣就永遠不會有衝突發生了

2.1 資源索引(resources.arsc)

最終生成的是資源索引表resources.arsc,resources.arsc是一個編譯後的二進制文件, 在AndroidStudio打開resources.arsc文件,以下所示

Android正是利用這個索引表根據資源ID進行資源的查找,爲不一樣語言、不一樣地區、不一樣設備提供相對應的最佳資源。查找和經過Resources和 AssetManger來完成的

在文中提到了兩個工具Apksigner 和 Jarsigner,下面一塊兒來了解一下Apksigner 和 Jarsigner的區別

Apksigner 和 Jarsigner的區別

在Android Studio中點擊菜單 Build->Generate signed apk... 打包簽名過程當中,能夠看到兩種簽名選項 V1(Jar Signature) V2(Full APK Signature)

  • Jarsigner是JDK提供的針對jar包簽名的通用工具
  • Apksigner是Google官方提供的針對Android apk簽名及驗證的專用工具

從Android 7.0開始, 谷歌增長新簽名方案 V2 Scheme (APK Signature),但Android 7.0如下版本, 只能用舊簽名方案 V1 scheme (JAR signing)

V1(Jar Signature)簽名:

來自JDK(Jarsigner),對zip壓縮包的每一個文件進行驗證, 簽名後還能對壓縮包修改(移動/從新壓縮文件),對V1簽名的apk/jar解壓,在META-INF存放簽名文件(MANIFEST.MF, CERT.SF, CERT.RSA), 其中MANIFEST.MF文件保存全部文件的SHA1指紋(除了META-INF文件), 由此可知: V1簽名是對壓縮包中單個文件簽名驗證

V2(Full APK Signature)簽名:

來自Google(apksigner), 對zip壓縮包的整個文件驗證, 簽名後不能修改壓縮包(包括zipalign), 對V2簽名的apk解壓, 沒有發現簽名文件, 從新壓縮後V2簽名就失效, 由此可知: V2簽名是對整個APK簽名驗證

建立發佈密鑰庫,請參閱在 Android Studio 中爲應用簽名

總結
  • V1簽名是對壓縮包中單個文件簽名驗證
  • V2簽名是對整個APK簽名驗證
  • zipalign能夠在V1簽名後執行
  • zipalign不能在V2簽名後執行,只能在V2簽名以前執行
  • V2簽名更安全(不能修改壓縮包)
  • V2簽名驗證時間更短(不須要解壓驗證), 於是安裝速度加快

注意: apksigner工具默認同時使用V1和V2簽名,以兼容Android 7.0如下版本

參考

相關文章
相關標籤/搜索