項目經驗,如需轉載,請註明作者:Yuloran (http://t.cn/EGU6c76)
原文
十分鐘搞定 Gradle - 掘金?juejin.im
前言
學習過程中,什麼階段最痛苦?大概是某個知識點的碎片信息學習了很多卻仍然無法窺其門徑,也就是似懂非懂的時候。對於 Gradle,筆者之前就是這種狀態。在親手完成了一個需求後,發現 Gradle 也不過如此。
由於筆者做需求時採用的是倒扒皮的方式,即先 google 搜索如何解決問題,再閱讀官方 User Guide,最後總結反思,所以用了半天的時間,還踩了一些坑。如果按照本文介紹,按部就班地學習,大概十分鐘就夠了。所謂一通則百通,窺其門徑後,若有其它需求,直接查閱 API 即可。
案例
筆者是做安卓整機開發的,目前接手了一個新項目,其 APP 分為兩個版本,一個是系統預置(private),一個供其它品牌手機安裝使用(public)。其中 public apk 需要打包到 private apk 的 assets 目錄下,以在 private apk 上實現掃碼安裝 public apk 的功能。兩個版本的代碼目前是手動維護,很不方便。筆者便想通過創建自定義的 Task,讓 Gradle 來自動構建。
問題
- 如何創建 private、public 兩個 build variants(構建變體)?
- 如何配置 public 版本在 private 版本之前構建(因為 private 版本依賴 public 版本生成的 apk)?
- public 版本構建完成後,如何自動複製其生成的 apk 到 private 版本的 assets 目錄下?
解決方案
- 關於構建變體,其實就是一次編譯,輸出多個版本的 apk,具體內容請參考官方文檔中文版《配置構建變體》
- 兩個構建變體,說明對應兩個 assemble task,那麼只要獲獲取到這兩個 task 對象,然後設置其依賴關係即可
- Gradle 文件支持 groovy 編寫,groovy 又是基於 java 的,所以即使不熟悉 groovy 的語法,也可以用 java 寫出來。不過對於複製這種操作,Gradle 有現成的 API
如何編寫
方案很清晰:assemblePublicApp -> deleteOldPublicApp -> signNewPublicApp -> copyNewPublicApp -> assemblePrivateApp
但是代碼怎麼寫呢?筆者一時間感到無從下手。比如如何獲取兩個構建變體對應的 assemble task?如何創建一個 copy task?又如何在執行 copy task 之前先執行 delete task(刪除 assets 目錄下的舊 apk) 以及 sign task(簽名 public apk)?
筆者一頓 google 搜索之後解決了這些問題,不過也踩了一個坑,就是自定義 task 內的代碼執行時機不對。比如 deleteOldPublicApk task 中的日誌,總是在執行 gradle assemble 命令之後立即輸出,而不是在 assemblePublicApp task 之後輸出:
File -> Demo/app/build.gradle
android {
...
}
task deleteOldPublicApk(type: Delete) {
println("-----------> delete the old pubic apk begin") // 注意:這麼寫代碼會在配置階段立即執行
delete src/privateApp/assets/Public.apk // delete 方法繼承自 Delete task,所以是一個 Action,在執行階段才會被執行
println("-----------> delete the old pubic apk end") // 注意:這麼寫代碼會在配置階段立即執行
}
task signNewPublicApp() {
doFirst {
println sign the new public app // 寫在 doFirst 或者 doLast 中,才會在執行階段被執行,具體見下文
}
}
task copyNewPublicApp() {
doLast {
println copy the new public app
}
}
afterEvaluate {
def assemblePublic = tasks.getByName(assemblePublicAppRelease)
deleteOldPublicApk.dependsOn(assemblePublic)
copyNewPublicApp.dependsOn(deleteOldPublicApk, signNewPublicApp)
def assemblePrivate = tasks.getByName(assemblePrivateApp)
assemblePrivate.dependsOn(copyNewPublicApp)
}
dependencies {
...
}
如上所示的 deleteOldPublicApk
task,只要在 terminal 中 輸入 gradlew assemble
必然會首先列印:
-----------> delete the old pubic apk begin
-----------> delete the old pubic apk end
相信很多不熟悉 Gradle 的人都會犯這樣的錯誤,stackoverflow 上有人也發出了同樣的疑問 Why is my Gradle task always running?
後來筆者閱讀了 Gradle 的官方文檔 《Build Lifecycle》,恍然大悟,應該這麼寫:
task deleteOldPublicApk(type: Delete) {
doFirst {
println("-----------> delete the old pubic apk begin")
}
delete src/privateApp/assets/Public.apk
doLast {
println("-----------> delete the old pubic apk old")
}
}
痛定思痛,筆者決定將 Gradle 的入門在此做一個總結。
入門
Gradle 的入門其實很簡單,不需要深入學習 Groovy(隨用隨查),也不用記 Gradle 的 API(隨用隨查)。只需要了解幾個核心概念(構建模型、構建的生命周期、Project、Task、TaskContainer),就能做到一通百通了。
構建模型的核心