Gradle 之創建 Build Variants

什么是 Gradle

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 的權利,使我們的版本管理更加靈活,方便。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。