最近接觸到自定義構建類型 BuildType,發現這一塊有些地方稍不注意的話會被繞進去浪費點時間,既然我這邊已經花費時間了,若是正好你也須要接觸到 BuildType,也許接下來分享的 tips 可能會幫你節省些時間。html
BuildType 相信許多開發者都不陌生,很常見的一種使用場景是線上、線下的後臺接口 BaseUrl 不一樣,許多人會選擇在 build.gradle 文件的 buildTypes 中定義全局變量來實現線上線下環境的定義(Gradle 2.x 版本),例如:java
buildTypes {
debug {
buildConfigField "String", "BASE_URL", "\"http://debug.api/\""
}
release {
buildConfigField "String", "BASE_URL", "\"https://release.api/\""
}
}
複製代碼
在開發過程當中,除了默認的 Debug 和 Release 版本,咱們可能還須要爲程序自定義一些東西。好比在上線 release 版本前,還須要一個預發佈版本,該版本除了後臺接口的 BaseUrl 與線上版本不一樣外,其餘資源(包括數據庫環境)都與線上相同,該版本用來作發佈前的最後測試,最大程度避免線上環境出問題。若是每次打預發版本都去直接修改代碼中的 BaseUrl 很明顯不是最優解。有一種解決方案是自定義 BuildType,在 app 模塊下的 build.gradle 的 buildTypes 中自定義新的構建版本:android
buildTypes {
debug {
buildConfigField "String", "BASE_URL", "\"http://debug.api/\""
}
release {
buildConfigField "String", "BASE_URL", "\"https://release.api/\""
}
pre.initWith(release)
pre {
buildConfigField "String", "BASE_URL", "\"https://pre.api/\""
}
}
//java 類中調用 BuildConfig.BASE_URL 獲取定義的變量
複製代碼
initWith()
是 BuildType 的一項配置項,咱們還能夠看到上文中提到的buildConfigField
其實也是一項配置項。該配置能夠理解成initWith(release)
能夠理解成拷貝了 release 這一構建類型的全部變量,由於咱們知道,每個構建類型都有一些默認的變量,例如debuggable
、zipAlignEnabled
等,使用該配置就免去爲新增的構建類型定義全部的變量。 定義了新的構建類型後,gradle會自動生成新的task,使用gradle assemblePre
便可打包新定義的預發包。這裏須要稍微注意的地方就是,必須在 app 模塊下的 build.gradle 中定義新的構建類型,gradle 纔會生成新的task。git
隨着如今模塊化開發愈來愈流行,許多項目都會將一些業務無關的模塊獨立出去,做爲 Moduel 在項目中依賴使用,以此達到複用的效果。很常見的例如網絡庫可能就會被獨立出來,那麼上文中的關於就會被定義在子 Module 中。這裏不注意的話就會浪費一些時間。假設你在 app 模塊與子模塊的 build.gradle 的 buildTypes 中都如上文定義了三種類型 debug 、release、pre 版本的BASE_URL
,請注意:github
若是你執行了gradle assemblePre
,沒錯是構建了 pre 版本,可是打印出日誌你會發現:數據庫
app.BuildConfig.BASE_URL = "https://pre.api/"
module.BuildConfig.BASE_URL = "https://release.api/"
複製代碼
子模塊中若是沒有特別指定構建版本,不管你執行的是gradle assemblePre
仍是gradle assembleDebug
,構建的都是 release 版本。可使用defaultPublishConfig
配置指定須要構建的版本,例如在子模塊的 build.gradle 中指定:api
android {
...
//指定構建版本
defaultPublishConfig "pre"
...
}
複製代碼
固然最好設置個變量,不然若是有許多子模塊,不可能修改構建版本時一個一個改過去,經常使用的是在項目最外層的 build.gradle 中設置變量供子模塊調用:bash
buildscript {
...
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
...
}
ext {
projectBuildType = "debug"
}
//子模塊中引用變量
defaultPublishConfig rootProject.ext.projectBuildType
複製代碼
這裏須要注意,一旦子模塊中指定了構建類型,例如 pre 版本,則該模塊的 buildTypes 中必須也要有對應的構建類型 pre,不然編譯不經過。而且,一旦指定了構建類型,則該模塊的構建類型就只會是指定的類型。舉個栗子:子模塊中指定了defaultPublishConfig "pre"
,執行gradle assembleRelease
,打印日誌:網絡
app.BuildConfig.BASE_URL = "https://release.api/"
module.BuildConfig.BASE_URL = "https://pre.api/"
複製代碼
因此這裏就會比較煩人,每次打不一樣的包都須要去修改projectBuildType
的值,仍是須要手動修改。app
想要隔離手動修改,網上看到過一種解決方案: 子模塊的 build.gradle 中設置:
android {
publishNonDefault true
}
複製代碼
而後在模塊的依賴處,例如 app 模塊的 build.gradle 中改進依賴的寫法:
releaseCompile project(path: ':module', configuration: 'release')
debugCompile project(path: ':module', configuration: 'debug')
複製代碼
這樣就能夠實現構建時子模塊與 app 模塊的類型一致。但這種寫法太煩了,若是你有十來個依賴,還得一個一個寫過去,又若是你還自定義了不止一種構建類型,且沒新增一個新的 buildType 都得修改全部的依賴,我認爲也不是最優解。
這裏提供一個方案僅供參考。先介紹一下咱們的開發流程,例如新開發1.0版本。首先從 master 切出一條 devel1.0 分支用於前期開發階段,開發完畢達到上線標準後,切出一條 pre1.0 預發分支,打預發包作最後測試並作最後的 bug 修復,最後測試經過,合併代碼到 master 分支,打線上包發佈至應用商店。不一樣階段對應不一樣的分支,因此不一樣的構建版本能夠經過分支名來決定,git 確定能夠獲取當前分支名,而 grale 中則能夠執行 cmd 命令,兩者結合便可達到想要的效果。有這個思路後實現起來就很簡單:
ext {
projectBuildType = "debug"
def gitBranchName = "git rev-parse --abbrev-ref HEAD".execute().text.trim()
if(gitBranchName.contains("master")) {
projectBuildType = "release"
} else if(gitBranchName.contains("pre")) {
projectBuildType = "pre"
}
}
複製代碼
Android Studio 3.0 + Gradle 3.0 相信許多人都躍躍欲試。升級到 Gradle 3.0 可能須要作一些改動,詳情可見Migrate to Android Plugin for Gradle 3.0.0。 Gradle3.0 中自定義 BuildType 有須要注意的地方
Cause of build error
Your app includes a build type that a library dependency does not.
在 Gradle 2.x 時代,若是 app 中定義了 pre 類型,而子模塊中沒有定義,是不會報錯的。但在 Gradle 3.0 下,若是你的 app 包含了新的自定義的 buildType,而依賴庫中卻沒有相應的自定義 buildType,則編譯階段就會報錯。
一種解決方案是在子模塊裏也定義 app 中的全部 buildType,固然,項目裏依賴多的同窗確定要吐槽了:我懶!不想修改辣麼多東西! Gradle 提供了比 2.x 時代更智能的兼容方案:matchingFallbacks
。
在 buildTypes 中定義 matchingFallbacks
,能夠在子模塊沒找到當前構建類型時指定要加載哪一個類型
//app 模塊下的 build.gradle 中
buildTypes {
debug {
buildConfigField "String", "BASE_URL", "\"http://debug.api/\""
}
release {
buildConfigField "String", "BASE_URL", "\"https://release.api/\""
}
pre.initWith(release)
pre {
buildConfigField "String", "BASE_URL", "\"https://pre.api/\""
matchingFallbacks = ['pre', 'debug', 'release']
}
}
複製代碼
matchingFallbacks 能夠定義多個構建類型,當執行gradle assemblePre
構建 Pre 版本時,而恰巧某個子模塊又沒有定義 pre 版本,則會按照你指定的 matchingFallbacks 從前日後依次尋找,直到類型匹配。這種匹配方案比 Gradle 2.x 時代默認爲你構建 release 版本要智能的多,至少咱們還能夠根據變量來在構建不一樣類型時定義不一樣的匹配順序。而且 Gradle 3.x 中前文提到的defaultPublishConfig
配置已經再也不生效了,構建時子模塊的構建類型與 app 構建類型保持一致(前提是子模塊中也定義了該類型),變得更加靈活。
技術終歸是在向前發展的。關於自定義 BuildType 的一些使用小貼士就是這些了,至於更多的關於構建類型相關的知識,例如 buildTypes 結合 productFlavors,或是定義 sourceSets 屬性指定不一樣的代碼目錄、資源文件目錄等知識之後有機會再開一篇聊吧(又挖坑2333)。
囉嗦了一堆,權且算是拋磚引玉。吼啦,下篇博客見~