升级Gradle3.0

概述

Android Gradle 3.0.0插件是一个大版本的升级,对多个module带来了显著的性能提升。但同时也改变了一些此插件的行为,DSL和APIS.

升级到Gradle3.0.0会带来以下的性能提升:

  • 由于构建per-call dexing提高了构建速度,每个类编译成独立的DEX文件,并且仅仅被修改的类重新编译。为了提高构建APK的速度,需要设置minSdkVersion到20,或者更低但使用 legacy multi-dex
  • 当改变依赖项时,Gradle通过不访问依赖项的API的module不重新编译的方式来提高构建速度。通过使用Gradle的新的依赖项的配置(implementation, api, compileOnly, and runtimeOnly),可以严格的控制依赖项的API的暴露。

升级

配置Gradle版本

Android plugin 3.0.0 要求Gradle4.1或者更高的版本。

需要在gradle-wrapper.properties文件中配置下列的内容:

1
2
3
distributionUrl=\
  https\://services.gradle.org/distributions/gradle-4.1-all.zip

配置Android Gradle Plugin

如果使用Android Studio 3.0或者更新版本,会提示自动更新到最新版本的Android plugin。对于手动更新工程,在工程级下的 build.gradle 配置如下的内容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
buildscript {
    repositories {
        ...
        // You need to add the following repository to download the
        // new plugin.
        google()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'
    }
}

NOTICE:

对于多模块和复合构建,如果Android插件每次构建不止一次加载,则可能会出现构建错误,可以查看常见错误进行修复。

新的依赖配置接口

问题描述

要理解旧的Gradle插件2.0构建系统的局限性,请考虑以下具有多层模块的项目:

多模块

看最底层的模块,基本上可以做出两种不同的改变:

  1. Implementation change: 内部实现改变,不修改模块的外部接口
  2. Application binary interface (ABI) change: 修改模块的外部接口

Note: 在下面的图形中,重新编译的模块将会以红色高亮

Implementation change

由于模块的外部接口不变,Gradle将只重新编译该模块。所有的引用的模块都将保持不变。

多模块

在这种情况下没问题。

ABI change

当外部模块的接口改变,那引用此模块的module也需要重新编译。

two_layout

但是这些模块可能直接通过自己的接口暴露底层模块的一部分!所以为了完全安全,还需要重新编译。

因此,Gradle将有效地需要重新编译所有模块。

final state

现在我们遇到了一个很大的问题:一个代码更改导致所有模块被重新编译。造成这种情况的根本原因是Gradle不知道是否通过另一个模块泄漏了此模块的接口。

Android Gradle插件3.0的修改

最新的Android Gradle 3.0插件要求您明确定义是否泄漏模块的接口。基于此来判断是否重新编译。

因此 compile 相关的接口被弃用了,引入了如下的接口:

  • api: 可以通过自己的接口泄漏了这个模块的接口,这意味着和旧版本的compile完全一致。
  • implementation: 模块的接口只能自己使用,不能泄露给更上层的module中。

api配置依赖

从理论上讲,你可以简单地用api依赖替换所有的编译依赖,但是仍然会导致所有的module都被重新编译。

final state

最好的方式是使用implementation代替所有的compile

implementation配置依赖

使用implementation会减少module重新编译。

implementation

Notice: 当想要泄漏底层的module接口给更上层的接口使用api

其他依赖配置

由于已经接口都已变化,团队也利用这个机会最终给予其他配置适当的名称:

  • provided 修改为compileOnly
  • apk 修改为 runtimeOnly
  • 其他的比如testCompile都已重新修改。

常见错误

Flavor相关的错误

1
2
Error:All flavors must now belong to a named flavor dimension.
The flavor 'flavor_name' is not assigned to a flavor dimension.

现在插件要求flavors必须属于一个flavor dimension,即使是只有一个flover。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Specifies two flavor dimensions.
flavorDimensions "tier"

productFlavors {
     free {
      dimension "tier"
    }

    paid {
      dimension "tier"
    }
}

依赖相关的配置错误

1
2
3
4
Error:Unable to resolve dependency for ':app@debug/compileClasspath':
  Could not resolve project :library.
Error:Unable to resolve dependency for ':app@release/compileClasspath':
  Could not resolve project :library.

使用变体的依赖关系解决方案,您不再需要使用特定于变体的配置(例如freeDebugImplementation)来获取本地模块依赖关系 - 插件会自动提供配置。

应该使用下面的配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
dependencies {
    // This is the old method and no longer works for local
    // library modules:
    // debugImplementation project(path: ':library', configuration: 'debug')
    // releaseImplementation project(path: ':library', configuration: 'release')

    // Instead, simply use the following to take advantage of
    // variant-aware dependency resolution. You can learn more about
    // the 'implementation' configuration in the section about
    // new dependency configurations.
    implementation project(':library')

    // You can, however, keep using variant-specific configurations when
    // targeting external dependencies. The following line adds 'app-magic'
    // as a dependency to only the "debug" version of your module.

    debugImplementation 'com.example.android:app-magic:12.3'
}
1
2
3
Process finished with exit code 1

Class not found: "com.meitu.gradle.eva.EvaChannelInfoPluginTest"Empty test suite.

是因为Android Studio没有把build/classes/groovy/test添加到 JUnitStarter的classpath中,具体问题以及临时解决方案看此链接 IDE didn't add build/classes/groovy/test to JUnitStarter classpath

1
2
3
Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

考虑一下应用程序配置了一个名为“staging”的build type,但是它的一个依赖库不存在此build type。 当编译应用程序的“staging”版本时,它将不知道要使用依赖库的哪个版本,并且会看到上述类似的错误。

Android Gradle Plugin包含DSL元素,可帮助控制Gradle解决应用程序和依赖项之间的变体匹配不可行的情况。 请参阅下表以确定应使用哪个DSL属性来解决与变体依赖关系匹配相关的某些构建错误。

  • app包含,但依赖库不包含

错误原因:

app包含一个依赖库不包含的build type, 比如,app包含“staging”的build type,但依赖库包含“debug”和“release”的build Type。

Notice: 当依赖库包含app不包含的build type时,不存在问题。这是因为根本不需要依赖库构建 build Type。

解决方案: 使用matchingFallbacks为给定的build type指定替代匹配,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
android {
    buildTypes {
        debug {}
        release {}
        staging {
            // Specifies a sorted list of fallback build types that the
            // plugin should try to use when a dependency does not include a
            // "staging" build type. You may specify as many fallbacks as you
            // like, and the plugin selects the first build type that's
            // available in the dependency.
            matchingFallbacks = ['debug', 'qa', 'release']
        }
    }
}
  • 特定flavor dimension,app存在而依赖库不存在的flover

错误原因:

对于app及其依赖库中存在的特定flavor dimension,但app包含库不包含的flavor。

比如,app和依赖都包含一个 "tier" 的flavor dimension。然而app中"tier" 包含 "free"和"paid",但依赖库仅仅包含"demo" and "paid"。

Notice: 对于存在于app及其依赖库的给定flavor dimension,当库包含app所不具备的flavor时,不存在任何问题。 那是因为android gradle plun根本就不会从依赖中请求app不包含的flavor。

解决方案:

使用matchingFallbacks为app的“free”的flavor指定替代匹配,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// In the app's build.gradle file.
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 'tier'
    productFlavors {
        paid {
            dimension 'tier'
            // Because the dependency already includes a "paid" flavor in its
            // "tier" dimension, you don't need to provide a list of fallbacks
            // for the "paid" flavor.
        }
        free {
            dimension 'tier'
            // 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 "tier" dimension.
            matchingFallbacks = ['demo', 'trial']
        }
    }
}
  • 依赖库包含app不支持的flavor dimension

错误原因:

依赖库包含app不支持的flavor dimension。

比如,依赖库包含“minApi”的flavor dimension,但是app不包含,当构建“freeDebug”版本的时候,插件不知道是使用“minApi23Debug”还是“minApi18Debug”的版本的依赖库

Notice: 当app包含一个flavor dimension,但依赖库不存在这是没问题的。这是因为插件仅仅匹配存在于依赖库里的flavor dimension,比如依赖库不包含ABIs的dimension,app的"freeX86Debug" 仅仅配置依赖库的“freeDebug”版本。

解决方案:

defaultConfig的配置中使用missingDimensionStrategy指定遗失的dimension。也可以在productFlavors来复写默认配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/ In the app's build.gradle file.
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
    // "minApi18" flavor. You can include additional flavor names to provide a
    // sorted list of fallbacks for the dimension.
    missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
    // You should specify a missingDimensionStrategy property for each
    // dimension that exists in a local dependency but not in your app.
    missingDimensionStrategy 'abi', 'x86', 'arm64'
    }
    flavorDimensions 'tier'
    productFlavors {
        free {
            dimension 'tier'
            // You can override the default selection at the product flavor
            // level by configuring another missingDimensionStrategy property
            // for the "minApi" dimension.
            missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
        }
        paid {}
    }
}

参考资料

  1. 迁移到Gradle3.0
  2. 配置构建变体
  3. Implementation vs API dependency
  4. Android Plugin for Gradle Release Notes
  5. Android Plugin DSL Reference
  6. Gradle官网
  7. Groovy官网
updatedupdated2020-06-132020-06-13