Gradle 4.1更新內容及注意事項

前言

進入2017年,Android Studio 版本更新至3.0,連帶着com.android.tools.build:gradle 工具也升級到了3.0.0,在3.0.0中使用了最新的Gralde 4.0 里程碑版本做爲gradle 的編譯版本,該版本gradle編譯速度有所加快,更加欣喜的是,徹底支持Java 8。固然,對於Kotlin的支持在這個版本也更加完善。進入12月份,谷歌又將 com.android.tools.build:gradle 版本更新到了3.0.1(Gradle 4.1),修復了一些 bug 並提高了啓動速度,在這裏咱們直接拿最新的4.1版本的特性做爲參照對象。android

 

一. 廢棄compile關鍵字

在 com.android.tools.build:gradle 3.0.0(即Gradle 4.0)版本中,compile關鍵字已經明確寫明廢棄了,可是google官方文檔上說「還會保留一段時間,直到下個比較大的gradle tools版本發佈」,因此如今使用compile暫時不會報錯,取而代之的是 api 關鍵字(做用等同於compile關鍵字)和 implementaion 關鍵字。具體修改規則修改以下:express

             

廢棄的緣由其實提及來很簡單——就是爲了加快工程的構建。api

爲了理解老版本Gradle 3.X構建系統的限制,這裏假設有個工程使用了多層module依賴方式。如圖所示:安全

             

對於最底部的基礎module,其將會有兩種可能的變化:app

  • Implementation 變化:不會改動本module對外暴露的接口。
  • pplication binary interface (ABI)變化:將會改變本module對外暴露的接口。(module指的是調用dependencymodule

注意:全部須要從新編譯的module將會在如下用紅色標出。eclipse

1. Implementation 變化(Gradle 4.X版本使用的依賴方式)

當本module依賴的ib(也能夠是module)發生變化時,因爲本module對外暴露的接口並不發生變化,在構建工程時gradle將會只從新編譯本module,全部依賴於本module的module並不會發生編譯。這種狀況是沒什麼問題的。如圖所示:jvm

           

2. ABI變化(Gradle 3.X 使用的依賴方式)

當本module依賴的ib(也能夠是module)發生變化時,本module向外暴露的接口發生了變化,那麼全部直接依賴於本module的module將不得不從新編譯。ide

        

接下來,這些上層module可能經過其自己的接口對外暴露了底層module的部份內容,即意味着本module暴露的接口也發生了變化,這會使得依賴於上層module的上上層module也須要從新編譯。這就致使了一個連鎖效應,所以,爲了絕對的安全起見,gradle將不得不從新編譯整個工程,使得編譯時間變得較長。如圖所示:工具

         

一點代碼的改動可能會引發整個工程的從新編譯,對構建效率的影響顯而易見,而實際上Gradle 4.0以前的版本的確是如此處理的,根本緣由就是gradle壓根不知道暴露的接口能夠經過一個接一個的依賴傳遞影響整個工程。測試

 

二. 新增長的api和implementation關鍵字

最新版的Gradle plugin須要你指出一個module的接口是否對外暴露其依賴lib的接口。基於此,可讓項目構建時,gradle能夠判斷哪一個須要從新編譯。所以,老版本的構建關鍵字compile被廢棄了,而是改爲了這兩個:

  • api:同compile做用同樣,即認爲本module將會泄露其依賴的module的內容。
  • implementation:本module不會經過自身的接口向外部暴露其依賴module的內容。 由此,咱們能夠明確的告訴gradle去從新編譯一個module,如果這個使用的module的接口發生變化的話。

 

三. 其餘關鍵字變化

  • provided關鍵字(對全部的build type以及favlors只在編譯時使用,相似eclipse中的external-libs,只參與編譯,不打包到最終apk) -> compileOnly
  • apk關鍵字(只會打包到apk文件中,而不參與編譯) -> runtimeOnly

 

四. 新特性:變體感知

之前咱們要建立不一樣版本的apk或者aar是使用productFlavorbuildType這兩個維度。若是須要更多維度就須要Gradle Android的新機制 dimension(維度):

  • 好比app的debug會自動消費他依賴的library的debug變體(Variant). 這個是很是古老的一個遺留問題了,終於解決了;
  • 須要爲全部flavor指定dimension(維度),不能直接匹配的須要提供matching fallbacks;
  • 這裏咱們理解一下dimension,他指的是維度,也就是咱們最終依靠這個維度去決定有哪些構建變體。同一個維度能夠有多個productFlavor,也就是咱們能夠爲多個productFlavor指定同一個維度。這樣,每一個維度下的Flavor數量的乘積就是最終構建變體(Variant)的數量(這裏能夠把buildType也認爲是一個維度).

1. 使用方式

//定義兩個風味維度
flavorDimensions "api", "mode"

productFlavors {
  	demo {
  		//指定風味維度
        dimension "mode"
        ...
  	}
  	
  	full {
        dimension "mode"
        ...
  	}
  	
  	minApi24 {
         dimension "api"
         minSDKVersion '24'
         versionNameSuffix "-minApi24"
  	}
  	
  	minApi23 {
  		dimension "api"
         minSDKVersion '23'
         versionNameSuffix "-minApi23"
  	}
  	
  	minApi21 {
  		dimension "api"
         minSDKVersion '21'
         versionNameSuffix "-minApi21"
  	}
}

如上,配置完後,Gradle建立的構建變體數量等於每一個風味維度(flavorDimension)中的風味(productFlavor)數量的乘積再乘以你配置的構建類型數量。以上面的構建配置爲例,Gradle 能夠建立的構建變體數量爲:3(api) x 2(mode) x 2(release和debug) = 12。

在 Gradle 爲每一個構建變體或對應 APK 命名時,屬於較高優先級風味維度的產品風味首先顯示,以後是較低優先級維度的產品風味,再以後是構建類型。

以上面的構建配置爲例,Gradle 可使用如下命名方案建立總共 12 個構建變體:

構建變體:minApi24, minApi23, minApi21[Debug, Release]

對應 APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk

例如構建變體:minApi24DemoDebug,對應 APK:app-minApi24-demo-debug.apk

這樣,你們大體知道這個維度究竟是個什麼東西了吧。就是咱們之前構建變體只有productFlavorbuildType這兩個維度,如今能夠定義任意多個維度了。

2. 過濾變體

若是有些變體不想要,能夠經過setIgnore(true)過濾掉他,這樣編譯也快一些:

android {
    ...
    variantFilter { variant ->
          def names = variant.flavors*.name
          // To check for a certain build type, use variant.buildType.name == "<buildType>"
          if (names.contains("minApi21") && names.contains("demo")) {
              // Gradle ignores any variants that satisfy the conditions above.
              setIgnore(true)
          }
      }
    ...
}

3. 遷移工程

以前咱們只有sitpro兩個flavor,那麼咱們只須要經過flavorDimensions定義一個名爲mode的dimension,而後給這兩個flavor都設置dimensionmode便可。這個dimension的名字也能夠本身起,好比能夠叫env表示是生產環境和測試環境。
若是以前沒有定義productFlavor就沒有必要修改了。

4. 構建變體的規則

優先級 
構建變體 > 構建類型 > 產品風味 > 主源集 > 庫依賴項 
其中: 
構建變體就是多個維度最終產生的組合拳. 
咱們把buildType也做爲一個dimension,他稱爲構建類型
而productFlavor中定義的dimension爲產品風味
主源集就是main目錄下面的資源了 
庫依賴項固然是各類依賴庫了.

例如: 
1. src/demoDebug/(構建變體源集) 
2. src/debug/(構建類型源集) 
3. src/demo/(產品風味源集) 
4. src/main/(主源集)

5. 構建變體時如何解決元素缺失問題

(1). 你的 Module App 包含了它所依賴的庫沒有的構建類型

若是依賴工程,不須要像之前那樣:

sitReleaseCompile project(path:':lib',configuration:'sitRelease')
proReleaseCompile ...
...

只須要下方寫法便可:

compile project(":lib")

他會自動按照構建類型去尋找。那要是找不到呢? 好比對應的lib中沒有對應的構建類型怎麼辦? 
好比咱們給Module app的buildType增長一個jniDebuggable類型以下:

jniDebug {
        jniDebuggable true
    }

而在app所依賴的lib中沒有這個構建類型,那自動依賴就會報錯。 這時候,咱們能夠指定matchingFallbacks表示找不到對應的依賴時能夠按配置中指定的順序找到第一個可用的,以下:

jniDebug {
    jniDebuggable true
    matchingFallbacks =['debug','release']
}

注意當依賴的庫中包含了Module App沒有的構建類型,則不會出現上述問題。

(2). 對於一個給定的存在於App和它所依賴的庫中的風味維度,咱們的主Module App包含了庫中沒有的風味

例如,主Module App和庫中都包含了一個mode的風味維度,咱們的App中指定mode維度的是free和paid風味,而庫中指定mode維度的是demo和paid風味,這時候咱們就能夠用`matchingFallbacks 來爲App中的free指定能夠替換的匹配項。以下:

android {
          defaultConfig{
          // Do not configure matchingFallbacks in the defaultConfig block.
          // Instead, you must specify fallbacks for a given product flavor in the
          // productFlavors block, as shown below.
        }
          flavorDimensions 'mode'
          productFlavors {
              paid {
                  dimension 'mode'
                  // Because the dependency already includes a "paid" flavor in its
                  // "mode" dimension, you don't need to provide a list of fallbacks
                  // for the "paid" flavor.
              }
              free {
                  dimension 'mode'
                  // Specifies a sorted list of fallback flavors that the plugin
                  // should try to use when a dependency's matching dimension does
                  // not include a "free" flavor. You may specify as many
                  // fallbacks as you like, and the plugin selects the first flavor
                  // that's available in the dependency's "mode" dimension.
                  matchingFallbacks = ['demo', 'trial']
              }
          }
      }

上述狀況中,若是說庫中包含了一個主Module App沒有的產品風味,則不會出現上述問題。

(3). 庫中包含了一個主Module App沒有的風味維度

例如,庫中聲明瞭一個minApi的風味維度,可是你的App中只有mode維度,所以當你要構建freeDebug這個變種版本的App時,插件就不知道你是想用minApi23Debug仍是用minApi25Debug變種版本的庫,這時候咱們能夠在主Module App中的defaultConfig代碼塊經過配置missingDimensionStrategy來讓插件從丟失的維度中指定默認的風味,固然你也能夠在productFlavors代碼塊中覆蓋先前的選擇,所以每個風味均可覺得丟失的維度指定一個不一樣的匹配策略。

android {
	defaultConfig{
          // Specifies a sorted list of flavors that the plugin should try to use from
          // a given dimension. The following tells the plugin that, when encountering
          // a dependency that includes a "minApi" dimension, it should select the
          // "minApi23" flavor. You can include additional flavor names to provide a
          // sorted list of fallbacks for the dimension.
          missingDimensionStrategy 'minApi', 'minApi23', 'minApi25'
    }
    flavorDimensions 'mode'
    productFlavors {
        free {
            dimension 'mode'
            // You can override the default selection at the product flavor
            // level by configuring another missingDimensionStrategy property
            // for the "minApi" dimension.
            missingDimensionStrategy 'minApi', 'minApi25', 'minApi23'
        }
        paid {}
    }          
}

這裏指的是若是若是依賴的工程中有這個minApi維度而主模塊沒有這個維度,則按照這個順序選擇依賴的flavor,好比優先選擇minApi,若是沒有minApi再選擇minApi23.

當你的主Module App中包含了一個庫中依賴項沒有的風味維度時,則不會出現上述問題。例如,當庫中依賴項不包含abi這個維度時,freeX86Debug版本將會使用freeDebug版本的依賴。

 

五. 其餘變化

1. API的變化

尤爲注意的是咱們重命名打包的APK文件,以及輸出路徑。變化前:

applicationVariants.all { variant ->
	variant.outputs.each { output ->
		def outputFile = output.outputFile
		if (outputFile != null && outputFile.name.endsWith('.apk')) {
			if (variant.buildType.name == 'lotteryTest') {
				def fileName = "myApp_v${defaultConfig.versionName}_${releaseTime()}.apk"
				output.outputFile = new File(outputFile.parent, fileName)
			}
		}
	}
}

變化後:

applicationVariants.all { variant ->
	variant.outputs.all { output ->
		def outputFile = output.outputFile
		if (outputFile != null && outputFile.name.endsWith('.apk')) {
			if (variant.buildType.name == 'lotteryTest') {
				def fileName = "myApp_v${defaultConfig.versionName}_${releaseTime()}.apk"
				outputFileName = new File(fileName)
			}
		}
	}
}

即咱們須要修改each() 和 outputFile() 方法爲 all() 和 outputFileName

2. 默認啓用AAPT2

在遷移的過程當中,若是發現因爲aapt2致使的異常,能夠在gradle.properties中加入:

android.enableAapt2=false

3. 支持Java8新特性

Gradle帶來全新的Java8支持方案desugar,啓用十分簡單,只須要配置下面代碼:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

若是你不想使用,也能夠禁用,能夠在gradle.properties中加入:

android.enableDesugar=false

記得刪除上面的兼容Java8代碼。

4. 移除Jack工具鏈,再也不支持

android {
	...
	defaultConfig {
		...
		// Remove this block.
		jackOptions {
			enabled true
			...
		}
	}
}

5. 移除Retrolambda插件

在project中的build.gradle中移除:

buildscript {
	...
	dependencies {
		// Remove the following dependency.
		classpath 'me.tatarka:gradle-retrolambda:<version_number>'
	}
}

Module級build.gradle文件:

// Remove the following plugin.
apply plugin: 'me.tatarka.retrolambda'
...
// Remove this block after migrating useful configurations.
retrolambda {
	...
	// If you have arguments for the Java VM you want to keep,
	// move them to your project's gradle.properties file.
	jvmArgs '-Xmx2048m'
}

6.目前兼容支持的功能特性

  • Lambda expressions
  • Method References
  • Type Annotations
  • Default and static interface methods
  • Repeating annotations
相關文章
相關標籤/搜索