????不管你是做移動開發(fā)肴盏、前端開發(fā)還是后端開發(fā),一定會遇到一個概念——打包帽衙,那打包是什么菜皂?為什么要打包?以及如何打包呢厉萝?
打包是什么恍飘,為什么要打包
????在我們?nèi)粘I钪杏心男┏R姷拇虬鼒鼍澳兀柯眯写虬吹妗⒖爝f打包章母、禮品打包、資料文檔打包翩剪,這些打包都是指把若干物品在空間上有機的組合在一起乳怎,對外呈現(xiàn)為一個整體的過程。 打包的目的是為了更方便前弯、安全地儲存蚪缀、運輸、呈現(xiàn)和使用物品恕出。
????在技術(shù)領(lǐng)域依舊如此询枚,打包一般指將程序的源代碼、資源文件和依賴庫等整合成一個整體的過程剃根,打包是為了程序能夠更加方便哩盲、安全的使用(包括存儲、傳輸、復(fù)制廉油、部署惠险、安裝、運行等)抒线。
如何打包
????馬上過年了班巩,X廠要發(fā)放新年大禮包給員工,那制作過程大概是這樣的:確定大禮包物品清單以及包裝盒樣式→確定每個物品擺放位置和順序→按順序?qū)ξ锲愤M行擺放→封包→打上防偽標簽嘶炭,在這個打包過程中抱慌,其實還隱藏著另一個流程,那就是每個步驟所需要的環(huán)境和工具的準備眨猎。在技術(shù)領(lǐng)域抑进,打包過程亦是如此,接下來就Android打包過程具體展開:
Android 打包流程
????打包在Android上也叫APK構(gòu)建睡陪,打包就是把Android工程源碼變?yōu)锳PK的過程寺渗,使用的工具叫Gradle。首先我們得有個認知兰迫,每個APK構(gòu)建的大流程是確定的信殊,只是不同的APP依賴三方庫(物品清單一部分)、簽名(防偽手段)汁果、使用工具集涡拘、打包后存放位置一些自定的地方不同,我們才需要進行對打包控制文件(build.gradle)進行配置据德。我們覺得打包流程不容易理解是因為我們能夠操作的build.gradle等文件都只是配置文件鳄乏,打包的內(nèi)部邏輯和流程是對我們隱藏的。打包跟其他任何工作一樣都是分為三步走:
????確定工作內(nèi)容和范圍→確定工作分工和順序→順序執(zhí)行每個任務(wù)棘利,分別對應(yīng)上述流程汞窗,Android打包也分為三個大的步驟:
- 初始化(Initialization)
- 配置(Configuration)
- 執(zhí)行(Execution)
我們在setting和build.gradle的配置也是在初始化和配置階段生效的。下面我們逐個進行介紹
一 赡译、初始化 Initialization
- 從工程源碼根目錄開始查找setting.gradle文件;
- 根據(jù)setting.gradle文件中的配置來決定哪些模塊參與構(gòu)建不铆;
- 首先為根build.gradle生成一個Project實例蝌焚,接下來為每一個setting.gradle中配置的模塊(對應(yīng)build.gradle)生成一個Project實例,根build.gradle對應(yīng)的Project是其他build.gradle對應(yīng)Project的父Project誓斥;
注意:
????① 因為每個build.gradle 都對應(yīng)一個Project對象只洒,Project對象類似于Android中的 Context,在build.gradle中獲取gradle系統(tǒng)方法都是通過內(nèi)置的project對象或者 getProject() 得到上下文后去獲取劳坑。
????② 這個階段我們配置的task毕谴、回調(diào)都不會被執(zhí)行,可以認為是Android中的類加載到內(nèi)存和實例對象初始化階段。
二涝开、配置 Configuration
1. 對每個build.gradle對應(yīng)的Project進行配置循帐,所謂的配置主要是指 ① 下載并初始化插件 ②配置 Android 的編譯版本、簽名舀武、產(chǎn)品風味等 ③ 下載依賴庫 ④注冊自定義Task 等拄养,其實這四個主要事情也是一個build.gradle中所有的內(nèi)容。project配置和執(zhí)行都是采用廣度優(yōu)先遍歷银舱,那就是父project先執(zhí)行瘪匿,然后是按照Setting.gradle中順序執(zhí)行子project, 這個階段也叫 Project Evaluation
2. 執(zhí)行Project evaluation的回調(diào)寻馏,比如Project.beforeEvalution 棋弥、Project.afterEvalution等;
3. 廣度優(yōu)先順序為每個Project注冊task诚欠,執(zhí)行task create回調(diào)(如果有)顽染,示例如下:
tasks.whenTaskAdded { task ->
println("hahaha task added call back name= "+task.name)
}
4. 為每一個Task創(chuàng)建一個依賴圖,依賴圖就是來說明運行這個task要先執(zhí)行哪些Task的圖聂薪;
5. 當所有Task的依賴圖構(gòu)建完畢家乘,發(fā)送 task構(gòu)建圖完成通知。如果用戶定義了回調(diào)就可以收到
project.gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
@Override
void graphPopulated(TaskExecutionGraph graph) {
println("hahaha task TaskExecutionGraph call back")
}
})
小結(jié):配置就是決定打包的具體內(nèi)容藏澳、使用工具和詳細流程仁锯。我們大部分在build.gradle中自定義的東西都是在配置階段生效和執(zhí)行。
注意:
- 我們在Android studio執(zhí)行sync的時候其實就是執(zhí)行了初始化+配置翔悠,我們配置了更多的東西比如更多三方庫业崖,更多構(gòu)建變體 這個sync時間就會更長;
- 執(zhí)行任何Task都要重新執(zhí)行整個項目的初始化和配置階段蓄愁,但是如果之前執(zhí)行過sync或者這兩個步驟双炕,而且所有的buid.gradle都未變化,gradle內(nèi)部會找到之前的緩存并跳過一個個任務(wù)的執(zhí)行撮抓。
三妇斤、執(zhí)行:Execution
????其實前面兩個階段都是讀入決策和準備階段,真正做事都是在這個階段——打包(也叫構(gòu)建丹拯、assemble)站超,根據(jù)不同的構(gòu)建類型和產(chǎn)品風味會有多個打包產(chǎn)物,接下來我們就指對最常見的assembleRelease進行展開乖酬。
????本文前面講所謂打包就是把Android App源碼變?yōu)锳PK的過程死相,Android App工程源碼是什么,結(jié)構(gòu)是怎么樣的咬像,做Android開發(fā)的應(yīng)該都比較清晰算撮,主要有AndroidManifest配置文件生宛、java代碼、kotlin代碼肮柜、XML陷舅、圖片、依賴庫地址素挽、本地庫(jar蔑赘、aar、so)等预明,這里就展開介紹缩赛;而APK其實就是一個包含編譯后的代碼和資源的壓縮文件,我們找到一個APK(改后綴為.zip)解壓得到文件列表如下:
重點模塊介紹如下:
1撰糠、META-INF
????META是Meta Data的縮寫酥馍,元數(shù)據(jù)是描述數(shù)據(jù)的數(shù)據(jù),APK是一個壓縮包阅酪,可以認為是一種數(shù)據(jù)結(jié)構(gòu)旨袒,那它的Meta-data就是描述這個APK相關(guān)信息的數(shù)據(jù),比如使用三方庫版本號术辐、JAR包簽名(V1簽名文件砚尽,V2簽名等會是簽名在包裝APK之上,打開后就破壞了這個簽名)等辉词。
2必孤、res
????res目錄下包含了整個APP工程的資源,就是說打包時候會把一個APP所有模塊瑞躺,依賴庫中的資源挑選出來敷搪,進行合并 、縮減(比如開啟R8優(yōu)化幢哨,工程種沒有使用的資源就不會打包到APK)赡勘,重命名等操作,最后整理出來一份資源放這里捞镰,它對應(yīng)著我們工程目錄中的資源目錄:
3闸与、AndroidManifest.xml
????AndroidManifest.xml包含了整個APP所有模塊的配置文件,就是說打包時候會把一個APP所有模塊岸售、依賴庫中的AndroidManifest文件拿出來合并几迄、優(yōu)化得到這一份總的AndroidManifest配置文件。
4冰评、classes.dex
????classes.dex 文件是 對應(yīng)Android 工程中的Java和Kotlin源碼,是他們經(jīng)過編譯后生成的 Android虛擬機(Dalvik木羹、ART) Executable(可執(zhí)行) 文件甲雅,.dex 后綴就是 Dalvik Excecutable的縮寫解孙。
5、resources.arsc
????resources.arsc是所有模塊的資源地址映射表合集抛人, 就是說我們在代碼中尋找資源是 R.id.XX弛姜,但是實際讀一個文件是需要文件路徑的,這個文件就是把Id映射為路徑的表妖枚。
6廷臼、assets
????assets目錄就是我們原來放在各個模塊assets目錄下的so、圖片绝页、apk等文件的合集荠商,這部分是直接移動復(fù)制過來,沒有優(yōu)化续誉、沒有壓縮莱没,所以搞包體積優(yōu)化這地方是重點。
7酷鸦、lib目錄
????lib目錄存儲了整個工程的所有so文件饰躲,這些so文件來源于本模塊、依賴模塊臼隔、依賴庫等地方嘹裂,如果能夠確定每個渠道包對應(yīng)的設(shè)備使用CPU架構(gòu),可以在Android默配置摔握、flavor 中配置寄狼, 配置后就只打包需要的SO文件,使用這個配置能有效減少APK包體積大小如下圖:
接下來就談?wù)勥@些產(chǎn)物是如何生成的盒发,Android官方給出的App打包詳細流轉(zhuǎn)圖如下:
注意:
- 這些流程是配置階段決定好的例嘱,執(zhí)行階段就是按找之前決定的進行執(zhí)行;
- 只要沒有先后順序的任務(wù)都是可以并行執(zhí)行的宁舰,Gradle也是多線程工作的拼卵;
名詞解釋
AAPT
aapt 是Android Asset Packaging Tool的縮寫,是編譯和打包資源的工具蛮艰,在SDK的build-tools目錄下腋腮。
ApkBuilder
ApkBuilder 相當于一個壓縮工具,它可以將已經(jīng)編譯好的 Java 代碼壤蚜、資源文件即寡、AndroidManifest.xml等打包成一個 ZIP 文件,即 APK 文件
參考文檔
https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:build_phases
https://blog.csdn.net/qq_38056514/article/details/127292335
https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
https://juejin.cn/post/7113713363900694565
http://tools.android.com/tech-docs/new-build-system/build-workflow