Android APK 打包过程 MD
| MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
Android APK 打包流程 MD
目錄
目錄APK 的打包流程
整體流程
資源的編譯和打包
資源ID
資源索引
概況
具體打包過(guò)程
aapt階段
aidl階段
Java Compiler階段
dex階段
apkbuilder階段
Jarsigner階段
zipalign階段
APK 的打包流程
參考
Android的包文件APK分為兩個(gè)部分:代碼和資源,所以打包方面也分為資源打包和代碼打包兩個(gè)方面,這篇文章就來(lái)分析資源和代碼的編譯打包原理。
Android打包流程詳圖:
整體流程
APK整體的的打包流程如下圖所示:
具體說(shuō)來(lái):
- 通過(guò)AAPT工具進(jìn)行資源文件(包括AndroidManifest.xml、布局文件、各種xml資源等)的打包,生成R.java文件。
- 通過(guò)AIDL工具處理AIDL文件,生成相應(yīng)的Java文件。
- 通過(guò)Javac工具編譯項(xiàng)目源碼,生成Class文件。
- 通過(guò)DX工具將所有的Class文件轉(zhuǎn)換成DEX文件,該過(guò)程主要完成Java字節(jié)碼轉(zhuǎn)換成Dalvik字節(jié)碼,壓縮常量池以及清除冗余信息等工作。
- 通過(guò)ApkBuilder工具將資源文件、DEX文件打包生成APK文件。
- 利用KeyStore對(duì)生成的APK文件進(jìn)行簽名。
- 如果是正式版的APK,還會(huì)利用ZipAlign工具進(jìn)行對(duì)齊處理,對(duì)齊的過(guò)程就是將APK文件中所有的資源文件舉例文件的起始距離都偏移4字節(jié)的整數(shù)倍,這樣通過(guò)內(nèi)存映射訪問(wèn)APK文件的速度會(huì)更快。
上述流程都是Android Studio在編譯時(shí)調(diào)用各種編譯命令自動(dòng)完成的,具體說(shuō)來(lái),如下所示:
1、創(chuàng)建Android工程。
android create project \
-n packageTest2 \
-a MainActivity \
-k com.package.test2 \
-t android-23 \
-p ./PackageTest2
2、編譯R文件
aapt package \
-f \
-J ./gen \
-M ./AndroidManifest.xml \
-S ./res/ \
-I /Users/RadAsm/Library/AndroidSDK/sdk/platforms/android-23/android.jar
3、編譯源代碼文件
javac -source 1.6 \
-target 1.6 \
-cp /Users/RadAsm/Library/AndroidSDK/sdk/platforms/android-23/android.jar \
./src/com/packtest/test1/MainActivity.java ./src/com/packtest/test1/R.java \
-d ./gen/classes
4、編譯DEX文件
dx --dex \
--verbose \
--output ./gen/dex/packtest1.dex
./gen/classes/
5、生成APK文件
aapt package
-f \
-J ./gen \
-M ./AndroidManifest.xml \
-S ./res/ \
-I /Users/RadAsm/Library/AndroidSDK/sdk/platforms/android-23/android.jar \
-F ./output/res.apk
6、APK文件對(duì)齊
zipalign -v -p 4 packagetest_unsigned.apk packagetest_aligned_unsigned.apk
7、APK簽名
apksigner sign --ks my-release-key.jks my-app.apk
以上便是APK打包的整個(gè)流程,我們?cè)賮?lái)總結(jié)一下:
- 除了assets和res/raw資源被原裝不動(dòng)地打包進(jìn)APK之外,其它的資源都會(huì)被編譯或者處理;
- 除了assets資源之外,其它的資源都會(huì)被賦予一個(gè)資源ID;
- 打包工具負(fù)責(zé)編譯和打包資源,編譯完成之后,會(huì)生成一個(gè)resources.arsc文件和一個(gè)R.java,前者保存的是一個(gè)資源索引表,后者定義了各個(gè)資源ID常量。
- 應(yīng)用程序配置文件AndroidManifest.xml同樣會(huì)被編譯成二進(jìn)制的XML文件,然后再打包到APK里面去。
- 應(yīng)用程序在運(yùn)行時(shí)通過(guò)AssetManager來(lái)訪問(wèn)資源,或通過(guò)資源ID來(lái)訪問(wèn),或通過(guò)文件名來(lái)訪問(wèn)。
理解了整體的流程,我們?cè)賮?lái)看看具體的細(xì)節(jié)。
資源的編譯和打包
在分析資源的編譯和打包之前,我們先來(lái)了解一下Android程序包里有哪些資源。
我們知道Android應(yīng)用程序的設(shè)計(jì)也是代碼與資源相分離的,Android的資源文件可以分為兩大類:
assets:assets資源放在主工程assets目錄下,它里面保存一些原始的文件,可以以任何方式來(lái)進(jìn)行組織,這些文件最終會(huì)原封不動(dòng)的被打包進(jìn)APK文件中。
獲取asset資源也十分簡(jiǎn)單,如下所示:
InputStream is = getAssets.open("fileName");res:res資源放在主工程的res目錄下,這類資源一般都會(huì)在編譯階段生成一個(gè)資源ID供我們使用。
res資源包含了我們開發(fā)中使用的各種資源,具體說(shuō)來(lái):
- animator
- anim
- color
- drawable
- layout
- menu
- raw
- values
- xml
這些資源的含義大家應(yīng)該都很熟悉,這里就不再贅述。
上述9種類型的資源文件,除了raw類型資源,以及Bitmap文件的drawable類型資源之外,其它的資源文件均為文本格式的XML文件,它們?cè)诖虬倪^(guò)程中,會(huì)被編譯成二進(jìn)制格式的XML文件。這些二進(jìn)制格式的XML文件分別有一個(gè)字符串資源池,用來(lái)保存文件中引用到的每一個(gè)字符串,包括XML元素標(biāo)簽、屬性名稱、屬性值,以及其它的一切文本值所使用到的字符串。這樣原來(lái)在文本格式的XML文件中的每一個(gè)放置字符串的地方在二進(jìn)制格式的XML文件中都被替換成一個(gè)索引到字符串資源池的整數(shù)值,這寫整數(shù)值統(tǒng)一保存在
R.java類中,R.java會(huì)和其他源文件一起編譯到APK中去。
前面我們提到xml編寫的Android資源文件都會(huì)編譯成二進(jìn)制格式的xml文件,資源的打包都是由AAPT工具來(lái)完成的,資源打包主要有以下流程:
- 解析AndroidManifest.xml,獲得應(yīng)用程序的包名稱,創(chuàng)建資源表。
- 添加被引用資源包,被添加的資源會(huì)以一種資源ID的方式定義在R.java中。
- 資源打包工具創(chuàng)建一個(gè)AaptAssets對(duì)象,收集當(dāng)前需要編譯的資源文件,收集到的資源保存在AaptAssets對(duì)象對(duì)象中。
- 將上一步AaptAssets對(duì)象保存的資源,添加到資源表ResourceTable中去,用于最終生成資源描述文件resources.arsc。
- 編譯values類資源,這類資源包括數(shù)組、顏色、尺寸、字符串等值。
- 給bag、style、array這類資源分配資源ID。
- 編譯xml資源文件,編譯的流程分為四步:① 解析xml文件 ② 賦予屬性名稱資源ID ③ 解析屬性值 ④ 將xml文件從文本格式轉(zhuǎn)換為二進(jìn)制格式。
- 生成資源索引表resources.arsc。
資源ID
每個(gè)Android項(xiàng)目里都有有一個(gè)R.java文件,如下所示:
public final class R {//...public static final class anim {public static final int abc_fade_in=0x7f010000;}public static final class attr {public static final int actionBarDivider=0x7f020000;}public static final class string {public static final int actionBarDivider=0x7f020000;}//... }每個(gè)資源項(xiàng)后的整數(shù)就是資源ID,資源ID是一個(gè)4字節(jié)的無(wú)符整數(shù),如下所示:
- 最高字節(jié)是Package ID表示命名空間,標(biāo)明資源的來(lái)源,Android系統(tǒng)自己定義了兩個(gè)Package ID,系統(tǒng)資源命名空間:0x01 和 應(yīng)用資源命名空間:0x7f。
- 次字節(jié)是Type ID,表示資源的類型,例如:anim、color、string等。
- 最低兩個(gè)字節(jié)是Entry ID,表示資源在其所屬資源類型中所出現(xiàn)的次序。
資源索引
上面提到,最終生成的是資源索引表resources.arsc,Android正是利用這個(gè)索引表根據(jù)資源ID進(jìn)行資源的查找,為不同語(yǔ)言、不同地區(qū)、不同設(shè)備提供相對(duì)應(yīng)的最佳資源。查找是通過(guò)Resources和AssetManger來(lái)完成的,這個(gè)我們下面會(huì)講。
resources.arsc 是一個(gè)編譯后的二進(jìn)制文件,在Android Stduio里打開以后是這樣的,如下所示:
可以看到resources.arsc里存放了各類資源的索引參數(shù)和配置信息。
resources.arsc的文件格式如下所示:
注:整個(gè)文件都是有一系列chuck(塊)構(gòu)成的,chuck是整個(gè)文件的劃分單位,每個(gè)模塊都是一個(gè)chuck,chuck最前面是一個(gè)ResChunk_header的結(jié)構(gòu)體,用來(lái)描述整個(gè)chunk的信息,更多關(guān)于索引表格式的細(xì)節(jié),可以查閱源碼:
? ResourceTypes.h
resources.arsc 索引表從上至下文件格式依次為:
- 文件頭:數(shù)據(jù)結(jié)構(gòu)用ResTable_header來(lái)描述,用來(lái)描述整個(gè)文件的信息,包括文件頭大小,文件大小,資源包Package的數(shù)量等信息。
- 全局字符串池:存放所有的字符串,所以資源復(fù)用這些字符串,字符串里存放的是資源文件的路徑名和資源值等信息。全局字符串池分為資源類型(type)字符串池和
- 資源包:會(huì)有多個(gè)(例如:系統(tǒng)資源包、應(yīng)用資源包)。
資源包也被劃分為以下幾個(gè)部分:
- 包頭:描述資源包相關(guān)信息。
- 資源類型字符串池:存放資源的類型。
- 資源名稱字符串池:存放資源的名稱。
- 配置列表:存放語(yǔ)音、位置等手機(jī)配置信息,用來(lái)作為查找資源的標(biāo)準(zhǔn)。
從這里可以看到resources.arsc索引表存在很多常量池,常量池的使用目的也很明顯,就是提供資源的復(fù)用率,減少resources.arsc索引表的體積,提高索引效率。
概況
參考
Android APK是如何來(lái)的呢?
懷著這個(gè)問(wèn)題去查資料,發(fā)現(xiàn)了下邊這張圖。
解壓一個(gè)普通的apk文件后,解壓出來(lái)的文件包括:
- classes.dex:.dex文件
- resources.arsc:resources resources文件
- AndroidManifest.xml:AndroidManifest.xml文件
- res:uncompiled resources
- META-INF:簽名文件夾
- MANIFEST.MF文件:版本號(hào)以及每一個(gè)文件的哈希值(BASE64),包括資源文件。這個(gè)是對(duì)每個(gè)文件的整體進(jìn)行SHA1(hash)。
- CERT.SF:這個(gè)是對(duì)每個(gè)文件的頭3行進(jìn)行SHA1 hash。
- CERT.RSA:這個(gè)文件保存了簽名和公鑰證書。
具體打包過(guò)程
aapt階段
使用aapt來(lái)打包res資源文件,生成R.java、resources.arsc和res文件(二進(jìn)制 & 非二進(jìn)制如res/raw和pic保持原樣)
- res目錄,有9種子目錄
- R.java文件。里面擁有很多個(gè)靜態(tài)內(nèi)部類,比如layout,string等。每當(dāng)有這種資源添加時(shí),就在R.java文件中添加一條靜態(tài)內(nèi)部類里的靜態(tài)常量類成員,且所有成員都是int類型。
- resources.arsc文件。這個(gè)文件記錄了所有的應(yīng)用程序資源目錄的信息,包括每一個(gè)資源名稱、類型、值、ID以及所配置的維度信息。我們可以將這個(gè)文件想象成是一個(gè)資源索引表,這個(gè)資源索引表在給定資源ID和設(shè)備配置信息的情況下,能夠在應(yīng)用程序的資源目錄中快速地找到最匹配的資源。
aidl階段
AIDL,Android接口定義語(yǔ)言,Android提供的IPC的一種獨(dú)特實(shí)現(xiàn)。
這個(gè)階段處理.aidl文件,生成對(duì)應(yīng)的Java接口文件。
Java Compiler階段
通過(guò)Java Compiler編譯R.java、Java接口文件、Java源文件,生成.class文件。
dex階段
通過(guò)dex命令,將.class文件和第三方庫(kù)中的.class文件處理生成classes.dex。
apkbuilder階段
將classes.dex、resources.arsc、res文件夾(res/raw資源被原裝不動(dòng)地打包進(jìn)APK之外,其它的資源都會(huì)被編譯或者處理)、Other Resources(assets文件夾)、AndroidManifest.xml打包成apk文件。
Jarsigner階段
對(duì)apk進(jìn)行簽名,可以進(jìn)行Debug和Release 簽名。
zipalign階段
release mode 下使用 aipalign 進(jìn)行align,即對(duì)簽名后的apk進(jìn)行對(duì)齊處理。
Zipalign是一個(gè)android平臺(tái)上整理APK文件的工具,它對(duì)apk中未壓縮的數(shù)據(jù)進(jìn)行4字節(jié)對(duì)齊,對(duì)齊后就可以使用mmap函數(shù)讀取文件,可以像讀取內(nèi)存一樣對(duì)普通文件進(jìn)行操作。如果沒(méi)有4字節(jié)對(duì)齊,就必須顯式的讀取,這樣比較緩慢并且會(huì)耗費(fèi)額外的內(nèi)存。
在 Android SDK 中包含一個(gè)名為 zipalign 的工具,它能夠?qū)Υ虬蟮?app 進(jìn)行優(yōu)化。 其位于 SDK 的 \build-tools\23.0.2\zipalign.exe 目錄下
2019-2-18
轉(zhuǎn)載于:https://www.cnblogs.com/baiqiantao/p/10398653.html
總結(jié)
以上是生活随笔為你收集整理的Android APK 打包过程 MD的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 野猪妹为什么不主E?
- 下一篇: 2019/2/18 Python今日收获