一涎跨、Android 打包流程
1、apk 內(nèi)部組成
打包后的apk是一個壓縮包,解壓之后,內(nèi)容如下:
- res文件夾為編譯之后的資源文件
- resources.arsc是aapt(Android assets packaging tools) 生成的資源索引表。這個文件記錄了所有的應(yīng)用程序資源目錄的信息惯裕,包括每一個資源名稱温数、類型、值轻猖、ID以及所配置的維度信息帆吻。這個資源索引表在給定資源ID和設(shè)備配置信息的情況下,能夠在應(yīng)用程序的資源目錄中快速地找到最匹配的資源咙边。
- AndroidManifest.xml 是編譯之后的AndroidManifest文件(二進(jìn)制)
- lib為 app需要的so文件
- classes.dex Android平臺上的可執(zhí)行文件猜煮,Android虛擬機(jī)Dalvik支持的字節(jié)碼文件格式。
- 簽名文件夾
三個簽名證書(MANIFEST.MF败许、CERT.SF王带、CERT.RSA)。MANIFEST.MF文件是對每個文件的SHA-256-Digest市殷;CERT.SF是對MANIFEST.MF的二次SHA-256-Digest摘要愕撰;CERT.RSA這個文件保存了簽名和公鑰證書。
詳細(xì)可參考apk簽名原理
2醋寝、apk打包流程
無論我們怎么配置gradle文件去自定義打包搞挣,但是都是要走上圖所畫的七個流程。七個深綠色的橢圓代表了七個不可或缺的打包步驟音羞,并且每一個步驟都一個打包工具
1囱桨、使用aapt工具將res資源文件生成R.java文件
2、使用aidl工具將aidl文件生成對應(yīng)java文件
3嗅绰、使用javac命令編譯工程源代碼和上面兩步生成的文件舍肠,生成class文件
4、通過dex工具將class文件和第三方j(luò)ar包打成dex文件
5窘面、用res下編譯過的二進(jìn)制文件翠语,以及assets中的原始資源文件,以及dex文件,通過apkbuilder工具打包成apk文件
6、通過jarsigner對apk進(jìn)行簽名
7财边、利用zipalign工具對apk進(jìn)行字節(jié)對齊優(yōu)化操作
所用到的工具:
工具名稱 | 功能介紹 | 在操作系統(tǒng)中的路徑 |
---|---|---|
aapt | Android資源打包工具 | ${ANDROID_SDK_HOME}/build-tools/30.0.0/aapt |
aidl | Android接口描述語言轉(zhuǎn)化為.java文件的工具 | ${ANDROID_SDK_HOME}/build-tools/30.0.0/aidl |
javac | java Compiler java代碼轉(zhuǎn)class文件 | ${JDK_HOME}/javac或/usr/bin/javac |
dex | 轉(zhuǎn)化.class文件為Davik VM能識別的.dex文件 | ${ANDROID_SDK_HOME}/build-tools/30.0.0/dx |
apkbuilder | 生成apk包 | ???沒有找到 |
jarsigner | .jar文件的簽名工具 | ${JDK_HOME}/jarsigner或/usr/bin/jarsigner |
zipalign | 字節(jié)碼對齊工具 | ${ANDROID_SDK_HOME}/tools/zipalign |
彩蛋
zipalign 字節(jié)對齊:
zipalign 主要工作是將apk包進(jìn)行對齊處理肌括。使apk包中的所有資源文件,起始偏移為4字節(jié)的整數(shù)倍酣难,這樣通過mmap內(nèi)存映射訪問apk時的速度會更快们童。
為什么要以4字節(jié)整數(shù)倍為起始偏移?
在文件對齊后, 就可以使用mmap來直接讀寫apk文件
- 正常的IO讀寫需要借助內(nèi)核態(tài)的頁緩存, 也就是 磁盤 ->內(nèi)核頁緩存 ->用戶主存
- mmap借助映射可以跳過內(nèi)核頁緩存, 只需要磁盤 ->用戶主存
Linux是以page為單位管理內(nèi)存的,mmap也是以page為單位建立映射的鲸鹦,因此offset必須是按page size對齊的(不對齊的話就會映射失敾劭狻)。
offset對齊是用戶自己保證的馋嗜,length對齊是靠內(nèi)核來保證的齐板。
三、gradle task
上面涉及到的Android打包流程是以gradle task鏈的形式串聯(lián)起來的。
下面看一下常見的task
- task ':app:preBuild'
預(yù)構(gòu)建甘磨,準(zhǔn)備構(gòu)建橡羞,在項(xiàng)目根目錄下生成build文件夾,在app目錄下生成build文件夾
- task ':app:preReleaseBuild'
預(yù)構(gòu)建济舆,生成/app/build/intermediates/prebuild/release目錄卿泽,Release是構(gòu)建類型
- task ':app:compileReleaseAidl'
編譯aidl相關(guān)文件,通過aidl工具把.aidl文件編譯成.java文件 滋觉,生成的結(jié)果在/app/build/generated/source/aidl/release目錄
- task ':app:compileReleaseRenderscript'
編譯渲染腳本
- task ':app:checkReleaseManifest'
檢查清單AndroidManifest.xml签夭,生成/app/build/intermediates/check-manifest/release目錄
- task ':app:generateReleaseBuildConfig'
在/app/build/generated/source/buildConfig/release目錄生成BuildConfig.java,構(gòu)建配置類中有應(yīng)用ID椎侠、構(gòu)建類型第租、版本名稱等
- task ':app:prepareLintJar'
準(zhǔn)備檢查.jar文件
- task ':app:mainApkListPersistenceRelease'
主要的apk列表持久化
- task ':app:generateReleaseResValues'
在/app/build/generated/res/resValues/release目錄,生成/app/src/main/res/values目錄相應(yīng)的資源值
- task ':app:generateReleaseResources'
生成資源我纪,即生成color慎宾,string,style等resources
- task ':app:mergeReleaseResources'
合并資源浅悉,即合并color趟据,string,style等resources到values.xml
- task ':app:createReleaseCompatibleScreenManifests'
創(chuàng)建兼容屏幕清單术健,
- task ':app:processReleaseManifest'
處理清單汹碱,生成處理后的/app/build/intermediates/manifests/full/release/AndroidManifest.xml
- task ':app:splitsDiscoveryTaskRelease'
分裂發(fā)現(xiàn)任務(wù)
- task ':app:processReleaseResources'
- task ':app:javaPreCompileRelease'
預(yù)編譯.java文件
- task ':app:compileReleaseJavaWithJavac'
使用javac編譯.java文件,
- task ':app:compileReleaseNdk
通過ndk編譯.c或.cpp等相關(guān)文件
- task ':app:generateReleaseAssets'
生成資產(chǎn)assets
- task ':app:mergeReleaseAssets'
合并資產(chǎn)assets
- task ':app:transformClassesWithDexBuilderForRelease'
使用dex構(gòu)建器轉(zhuǎn)換.class文件苛坚,在/app/build/intermediates/transforms/dexBuilder/release目錄生成R.dex比被,BuildConfig.dex色难,MainActivity.dex等.dex文件
- task ':app:transformDexArchiveWithExternalLibsDexMergerForRelease'
使用外部庫dex合并器轉(zhuǎn)換.dex歸檔文件泼舱,在/app/build/intermediates/transforms/externalLibsDexMerger/release目錄生成classes.dex文
件
- task ':app:transformDexArchiveWithDexMergerForRelease'
使用dex合并器轉(zhuǎn)換.dex歸檔文件,
在/app/build/intermediates/transforms/dexMerger/release目錄生成classes.dex文件
- task ':app:mergeReleaseJniLibFolders'
合并jniLibs文件夾
- task ':app:transformNativeLibsWithMergeJniLibsForRelease'
轉(zhuǎn)換本地庫文件枷莉,合并jni庫文件
- task ':app:transformNativeLibsWithStripDebugSymbolForRelease'
轉(zhuǎn)換本地庫文件娇昙,剝離調(diào)試符號
- task ':app:processReleaseJavaRes'
處理Java資源,
- task ':app:transformResourcesWithMergeJavaResForRelease'
轉(zhuǎn)換資源笤妙,合并Java資源冒掌,生成resources.arsc
- task ':app:validateSigningRelease'
驗(yàn)證簽名
- task ':app:packageRelease'
打包,在/app/build/intermediates/incremental/packageRelease目錄生成相關(guān)文件
- task ':app:assembleRelease'
聚集蹲盘,組合股毫,最終生成/app/build/outputs/apk/release/app-release.apk
四、了解了apk的打包流程后,我們可以額外做哪些事情?
我可以想到的:
- 編寫注解和注解處理器,利用apt過程,為我們生成輔助代碼
-
在第4步,通過自定義Transform Task,在class文件轉(zhuǎn)換成dex文件前,對class文件做一些處理
如:利用ASM 動態(tài)的修改字節(jié)碼文件,幫忙我們實(shí)現(xiàn)我們需要的功能召衔。
Transform API
Starting with 1.5.0-beta1, the Gradle plugin includes a Transform API allowing 3rd party plugins to manipulate compiled class files before they are converted to dex files.
- hook 其他的task铃诬,造成干擾,以達(dá)成我們需要的效果。
如VirtualApk中 插件apk打包過程中 對插件依賴資源的做去重操作趣席;修改appt生成的R.java和Resources.arsc文件的ID起始位兵志。