什么是 Gradle
Gradle 是一個(gè)高級(jí)構(gòu)建工具。
圖片來(lái)自于 Gradle 官網(wǎng)莹妒,根據(jù)官網(wǎng)介紹,上圖從左往右赶站,依次體現(xiàn)了:Build Anything >> Automate Everything >> Deliver Faster. 據(jù)此,我們可以看到它的主要特點(diǎn):可以構(gòu)建的種類多纺念,自動(dòng)化構(gòu)建過(guò)程贝椿,快速交付。
Android Studio 就是使用了這種工具陷谱,來(lái)完成 APK 的打包過(guò)程烙博。如上面所說(shuō),這個(gè)過(guò)程原本是自動(dòng)化完成的烟逊。自動(dòng)化的東西都是流水線渣窜,我們總會(huì)有一些自己的需求,這時(shí)宪躯,就需要自定義配置這個(gè)過(guò)程图毕。
下面,主要討論下構(gòu)建變體的使用眷唉。
構(gòu)建變體的需求
先來(lái)說(shuō)一下這種需求予颤。
通常,在開發(fā)環(huán)境中冬阳,我們會(huì)使用 debug 版本的應(yīng)用蛤虐;在發(fā)布是時(shí)候使用 release 版本。
在 debug 版本中個(gè)肝陪,我們會(huì)有顯示日志的需求驳庭;在 release 版本中是不需要的,日志是為了方便我們調(diào)試氯窍,而且可能包含一些敏感數(shù)據(jù)饲常。
在 release 版本中,通常我們需要進(jìn)行代碼混淆狼讨,否則贝淤,很容易被別人反編譯,就像是一個(gè)人沒有穿衣服政供,顯然播聪,debug 版本就不需要。
從應(yīng)用功能上來(lái)講布隔,同一個(gè) APP 在發(fā)布的時(shí)候离陶,有時(shí)候會(huì)根據(jù)需要發(fā)布不同的版本,比如在不同的應(yīng)用市場(chǎng)上會(huì)有一些定制衅檀,即使是在同一個(gè)市場(chǎng)上招刨,可能也要提供付費(fèi)版和免費(fèi)版等等。
為了解決上述問(wèn)題哀军,不管是從構(gòu)建方式的角度沉眶,還是應(yīng)用功能差異性的角度構(gòu)建 apk 包打却,我們都需要對(duì)這些版本進(jìn)行管理。Gradle 就提供了這樣一個(gè)途徑沦寂,使得我們可以通過(guò)修改 構(gòu)建配置(文件) 的方式以滿足特定版本的需求学密,然后讓 Gradle 根據(jù)我們的選擇自動(dòng)構(gòu)建我們想要的應(yīng)用淘衙。
當(dāng)然传藏,處理問(wèn)題的方式不止一種,我們也會(huì)有很多替代方案彤守。但是毯侦,明明離終點(diǎn)只差一步,為什么還要翻過(guò)一座大山呢具垫?
什么是構(gòu)建變體
談完需求侈离,下面我們來(lái)講講什么是構(gòu)建變體,它是通過(guò) BuildType 和 BuildFlavor 組合實(shí)現(xiàn)的筝蚕。如下所示卦碾,共產(chǎn)生 6 個(gè)變體。
所謂 BuildType 和 BuildFlavor起宽,即構(gòu)建類型和構(gòu)建特征(或者叫產(chǎn)品風(fēng)味)洲胖。
BuildType,構(gòu)建類型坯沪,主要針對(duì)開發(fā)生命周期的不同階段進(jìn)行配置绿映。一個(gè)模塊或者項(xiàng)目,默認(rèn)有兩種類型腐晾,release 和 debug叉弦。 debug 類型下 debuggable 屬性是 true,從而使得我們可以打斷點(diǎn)進(jìn)行調(diào)試藻糖。debug 類型在打包的時(shí)候淹冰,會(huì)使用默認(rèn)的自動(dòng)生成的簽名,對(duì)于 release 類型來(lái)說(shuō)巨柒,發(fā)布的時(shí)候需要使用我們自己的密鑰進(jìn)行簽名榄棵。同時(shí),我們還可以在發(fā)布的時(shí)候潘拱,進(jìn)行代碼混淆疹鳄。
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芦岂,構(gòu)建特征瘪弓,主要是用以發(fā)布給用戶不同的應(yīng)用版本。需要注意的是禽最,這里的版本并非是版本號(hào)腺怯,而是功能袱饭。比如,我們上說(shuō)所說(shuō)的免費(fèi)版呛占、付費(fèi)版虑乖。
flavorDimensions "default"
productFlavors {
free {
dimension "default"
applicationIdSuffix ".free"
versionNameSuffix "-free"
buildConfigField "String", "NAME", "\"免費(fèi)版\""
}
paid {
dimension "default"
applicationIdSuffix ".paid"
versionNameSuffix "-paid"
buildConfigField "String", "NAME", "\"付費(fèi)版\""
}
cmpy {
dimension "default"
applicationIdSuffix ".cmpy"
versionNameSuffix "-cmpy"
buildConfigField "String", "NAME", "\"內(nèi)部使用\""
}
}
如何使用構(gòu)建變體
上一節(jié),說(shuō)了什么是構(gòu)建變體晾虑,但是疹味,我們?cè)撛趺词褂靡詽M足我們的需求呢?有兩個(gè)地方帜篇,給我們帶來(lái)了可能性:BuildConfig 和 SourceSet(源集)糙捺。
BuildConfig
也許你已經(jīng)注意到了上面 productFlavor 中的
buildConfigField "String", "NAME", "\"免費(fèi)版\""
事實(shí)上,對(duì)于每一種變體笙隙,都會(huì)有一個(gè) BuildConfig 與之一一對(duì)應(yīng)洪灯。
我們來(lái)看看構(gòu)建變體 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 = "免費(fèi)版";
}
這些字段都是靜態(tài)常量,在項(xiàng)目中竟痰,我們都可以通過(guò) BuildConfig 直接訪問(wèn)签钩。比如,你可以通過(guò) BuildConfig.DEBUG 判斷當(dāng)前版本是否是 debug 版本坏快;可以通過(guò) BuildConfig.FLAVOR 判斷當(dāng)前的 productFlavor铅檩,已決定是否啟用某個(gè)功能。當(dāng)然假消,我們也可以自定義字段柠并,比如:
buildConfigField "String", "NAME", "\"免費(fèi)版\""
上述這些字段,無(wú)論是自定義的富拗,還是默認(rèn)的臼予,都可以成為我們?cè)陧?xiàng)目中的判斷條件。
SourceSet
所以源集啃沪,即代碼和資源的分組粘拾。這可能有點(diǎn)抽象,舉個(gè)栗子创千,src/main 就是一個(gè)源集缰雇。
每個(gè) buildType 可以有一個(gè)源集,每個(gè) buildFlavor 可以有一個(gè)源集追驴,每個(gè) buildVariant 同樣可以有一個(gè)源集械哟。
其中,src/main 這個(gè)源集是所有源集所共有的殿雪,除此之外暇咆,源集之間互斥。
如上圖所示,一共有三個(gè)源集:freeDebug爸业、freeRelease其骄、main。
注意扯旷,freeDebug拯爽、freeRelease 實(shí)際上是使用 buildVariant 的構(gòu)建的源集,需要和構(gòu)建變體的名字一樣(當(dāng)然可以通過(guò)配置修改這種默認(rèn)行為)钧忽。
這里有幾點(diǎn)注意事項(xiàng):
- 對(duì)于 java 文件來(lái)講毯炮,freeDebug 中不能出現(xiàn)和 main 中一樣在同一個(gè)包下類名相同的 java 文件,因?yàn)樗麄冎g是共享關(guān)系惰瓜,相當(dāng)于將 main 中的所有 java 和 freeDebug 合并在一起否副。如果出現(xiàn)兩個(gè)相同名字的 java 文件汉矿,會(huì)報(bào)錯(cuò)崎坊。freeDebug 和 freeRelease 不同,它們所有的資源都是互斥的洲拇,相互不影響奈揍,因?yàn)槲覀冊(cè)跇?gòu)建的時(shí)候,也只能選擇其中一種進(jìn)行構(gòu)建赋续。假設(shè)我們選擇了 freeDebug男翰,那么 freeRelease 就會(huì)被剔除在外。
- 對(duì)于 res 文件夾下的這些資源文件纽乱,freeDebug 和 freeRelease 這些源集之間同樣是互斥的蛾绎,相互沒有任何影響。但是對(duì)于不同源集和 main之間鸦列,卻存在著資源合并或替換的情況租冠。
對(duì)于 drawable 及其相關(guān)文件夾下的圖片而言,freeDebug 中的圖片會(huì)直接覆蓋 main 中的同名圖片薯嗤。對(duì)于 layout 文件夾下的布局文件也是這樣顽爹。
但是,對(duì)于 values 文件夾下的 xml 文件來(lái)說(shuō)骆姐,確是文件內(nèi)容的合并镜粤。比如在源集 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>
最后,非常重要的一點(diǎn)玻褪,合并或者替換時(shí)會(huì)遵照優(yōu)先級(jí):
buildVariant > buildType > buildFlavor> main > 庫(kù)依賴項(xiàng)
總結(jié)
通過(guò) Gradle 創(chuàng)建 構(gòu)建變體肉渴,等于說(shuō)給了我們告知 Gradle 選擇哪些 class 哪些資源編譯打包成 apk 的權(quán)利,使我們的版本管理更加靈活带射,方便同规。