什么是 Gradle
Gradle 是一個高級構建工具。
圖片來自于 Gradle 官網,根據官網介紹,上圖從左往右,依次體現了:Build Anything >> Automate Everything >> Deliver Faster. 據此,我們可以看到它的主要特點:可以構建的種類多,自動化構建過程,快速交付。
Android Studio 就是使用了這種工具,來完成 APK 的打包過程。如上面所說,這個過程原本是自動化完成的。自動化的東西都是流水線,我們總會有一些自己的需求,這時,就需要自定義配置這個過程。
下面,主要討論下構建變體的使用。
構建變體的需求
先來說一下這種需求。
通常,在開發環境中,我們會使用 debug 版本的應用;在發布是時候使用 release 版本。
在 debug 版本中個,我們會有顯示日志的需求;在 release 版本中是不需要的,日志是為了方便我們調試,而且可能包含一些敏感數據。
在 release 版本中,通常我們需要進行代碼混淆,否則,很容易被別人反編譯,就像是一個人沒有穿衣服,顯然,debug 版本就不需要。
從應用功能上來講,同一個 APP 在發布的時候,有時候會根據需要發布不同的版本,比如在不同的應用市場上會有一些定制,即使是在同一個市場上,可能也要提供付費版和免費版等等。
為了解決上述問題,不管是從構建方式的角度,還是應用功能差異性的角度構建 apk 包,我們都需要對這些版本進行管理。Gradle 就提供了這樣一個途徑,使得我們可以通過修改 構建配置(文件) 的方式以滿足特定版本的需求,然后讓 Gradle 根據我們的選擇自動構建我們想要的應用。
當然,處理問題的方式不止一種,我們也會有很多替代方案。但是,明明離終點只差一步,為什么還要翻過一座大山呢?
什么是構建變體
談完需求,下面我們來講講什么是構建變體,它是通過 BuildType 和 BuildFlavor 組合實現的。如下所示,共產生 6 個變體。
所謂 BuildType 和 BuildFlavor,即構建類型和構建特征(或者叫產品風味)。
BuildType,構建類型,主要針對開發生命周期的不同階段進行配置。一個模塊或者項目,默認有兩種類型,release 和 debug。 debug 類型下 debuggable 屬性是 true,從而使得我們可以打斷點進行調試。debug 類型在打包的時候,會使用默認的自動生成的簽名,對于 release 類型來說,發布的時候需要使用我們自己的密鑰進行簽名。同時,我們還可以在發布的時候,進行代碼混淆。
signingConfigs {
release {
storeFile file("xxxx.jks")
storePassword "xxxx"
keyAlias "xxxx"
keyPassword "xxxx"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
}
//這是自定義的 buildType,繼承自 debug
/*jnidebug {
initWith debug
applicationIdSuffix ".jnidebug"
jniDebuggable true
}*/
}
BuildFlavor,構建特征,主要是用以發布給用戶不同的應用版本。需要注意的是,這里的版本并非是版本號,而是功能。比如,我們上說所說的免費版、付費版。
flavorDimensions "default"
productFlavors {
free {
dimension "default"
applicationIdSuffix ".free"
versionNameSuffix "-free"
buildConfigField "String", "NAME", "\"免費版\""
}
paid {
dimension "default"
applicationIdSuffix ".paid"
versionNameSuffix "-paid"
buildConfigField "String", "NAME", "\"付費版\""
}
cmpy {
dimension "default"
applicationIdSuffix ".cmpy"
versionNameSuffix "-cmpy"
buildConfigField "String", "NAME", "\"內部使用\""
}
}
如何使用構建變體
上一節,說了什么是構建變體,但是,我們該怎么使用以滿足我們的需求呢?有兩個地方,給我們帶來了可能性:BuildConfig 和 SourceSet(源集)。
BuildConfig
也許你已經注意到了上面 productFlavor 中的
buildConfigField "String", "NAME", "\"免費版\""
事實上,對于每一種變體,都會有一個 BuildConfig 與之一一對應。
我們來看看構建變體 free.debug 的BuildConfig:
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.ygs.test.free.debug";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "free";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0-free";
// Fields from product flavor: free
public static final String NAME = "免費版";
}
這些字段都是靜態常量,在項目中,我們都可以通過 BuildConfig 直接訪問。比如,你可以通過 BuildConfig.DEBUG 判斷當前版本是否是 debug 版本;可以通過 BuildConfig.FLAVOR 判斷當前的 productFlavor,已決定是否啟用某個功能。當然,我們也可以自定義字段,比如:
buildConfigField "String", "NAME", "\"免費版\""
上述這些字段,無論是自定義的,還是默認的,都可以成為我們在項目中的判斷條件。
SourceSet
所以源集,即代碼和資源的分組。這可能有點抽象,舉個栗子,src/main 就是一個源集。
每個 buildType 可以有一個源集,每個 buildFlavor 可以有一個源集,每個 buildVariant 同樣可以有一個源集。
其中,src/main 這個源集是所有源集所共有的,除此之外,源集之間互斥。
如上圖所示,一共有三個源集:freeDebug、freeRelease、main。
注意,freeDebug、freeRelease 實際上是使用 buildVariant 的構建的源集,需要和構建變體的名字一樣(當然可以通過配置修改這種默認行為)。
這里有幾點注意事項:
- 對于 java 文件來講,freeDebug 中不能出現和 main 中一樣在同一個包下類名相同的 java 文件,因為他們之間是共享關系,相當于將 main 中的所有 java 和 freeDebug 合并在一起。如果出現兩個相同名字的 java 文件,會報錯。freeDebug 和 freeRelease 不同,它們所有的資源都是互斥的,相互不影響,因為我們在構建的時候,也只能選擇其中一種進行構建。假設我們選擇了 freeDebug,那么 freeRelease 就會被剔除在外。
- 對于 res 文件夾下的這些資源文件,freeDebug 和 freeRelease 這些源集之間同樣是互斥的,相互沒有任何影響。但是對于不同源集和 main之間,卻存在著資源合并或替換的情況。
對于 drawable 及其相關文件夾下的圖片而言,freeDebug 中的圖片會直接覆蓋 main 中的同名圖片。對于 layout 文件夾下的布局文件也是這樣。
但是,對于 values 文件夾下的 xml 文件來說,確是文件內容的合并。比如在源集 main 中的 strings.xml 中
<resources>
<string name="app_name">App</string>
<string name="hello_world">Hello world!</string>
</resources>
在源集 freeDebug 中的 strings.xml 中
<resources>
<string name="app_name">FreeDebug</string>
</resources>
它們合并之后就是
<resources>
<string name="app_name">FreeDebug</string>
<string name="hello_world">Hello world!</string>
</resources>
最后,非常重要的一點,合并或者替換時會遵照優先級:
buildVariant > buildType > buildFlavor> main > 庫依賴項
總結
通過 Gradle 創建 構建變體,等于說給了我們告知 Gradle 選擇哪些 class 哪些資源編譯打包成 apk 的權利,使我們的版本管理更加靈活,方便。