技术成就梦想

Android Studio 升级到3.0后的gradle迁移(bing译文) 原 Android Studio 升级到3.0后的gradle迁移(bing译文)



首先推荐按照官方版进行,如有疑问,评论中讨论;

官方步骤Migrate to Android Plugin for Gradle 3.0.0

英文水平高的可直接点击链接查看原文,以防翻译出错;

Gradle 3.0.0 的 Android 插件是一个主要的升级, 为大型多项目带来显著的性能改进。为了实现这些改进, 在插件行为、DSL 和 api 方面有一些突破性的变化。

对于大多数项目, 在您更新 Gradle 并应用最新版本的 Android 插件后, 您将不会遇到此页中描述的任何一个或仅有几个生成错误。如果在更新插件后遇到生成错误, 只需在该页中搜索错误输出或导航到相关主题, 然后按照说明解决问题。还可以查看以下视频以了解迁移步骤的概述。

迁移项目后, 可以从以下性能改进中获益:

通过细粒度任务图更好地多项目的并行性。 具有变体意识的依赖性管理。当构建模块的某个变体时, 该插件现在会自动将本地库模块依赖项的变体与您正在构建的模块的变体相匹配。 在对依赖项进行更改时, Gradle 通过不编译不能访问该依赖项 API 的模块来执行更快的生成。通过使用 Gradle 的新的依赖项配置 (实现、api、compileOnly 和 runtimeOnly), 可以限制哪些依赖项将其 api 泄漏给其他模块。 由于每级的德兴而加快了增量生成速度。每个类现在被编译成单独的德克斯文件, 只有被修改的类才编译。您还应该期待改进的应用程序的构建速度, 将 minSdkVersion 设置为20或更低, 并使用旧式 multi-dex。 有关更新和更改的更完整列表, 请阅读 Android 插件3.0.0 发行说明。

要了解有关当前正在处理的问题的更多信息, 请参见已知问题。

更新Gradle 版本

Android 插件3.0.0 要求 Gradle 版本4.1 或更高。如果您正在使用 Android Studio 3.0 或更高版本打开现有项目, 请按照提示自动将现有项目更新为兼容的 Gradle。

要手动更新 Gradle, 请在 Gradle 包装中编辑 URL. 属性为以下内容:

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

应用新的插件

如果您正在使用 android 工作室3.0 或更高版本打开现有项目, 请按照提示自动将您的项目更新为最新版的 android 插件。要手动更新项目, 请包括 maven 回购, 并在项目级生成. gradle 文件中更改插件版本, 如下所示:

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'
    }
}

注意: 对于多和复合生成, 如果 Android 插件在每个版本中加载一次以上, 则可能会出现生成错误。要了解更多内容, 请阅读已知问题

使用具有变体意识的依赖关系管理

Android 插件3.0.0 和更高的包括一个新的依赖机制, 自动匹配的变体时, 使用库。这意味着应用程序的调试变体会自动消耗库的调试变体, 等等。它也工作时, 使用的味道-一个应用程序的 freeDebug 变体将消耗一个库的 freeDebug 变体。

为了使插件准确地匹配变体, 您需要声明所有产品口味的风味尺寸, 并为无法直接匹配的实例提供匹配的退。

声明变体纬度(dimention)

该插件现在要求所有的口味属于一个命名的味道维度-即使你打算只使用一个单一的维度。否则, 您将得到以下生成错误:

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

要解决此错误, 您需要首先使用 flavorDimensions 属性声明一个或多个维度。然后, 将每个味道分配给您声明的一个维度, 如下面的示例所示。由于插件自动匹配您的依赖项, 因此应仔细命名您的风味尺寸。这样做使您可以更好地控制来自本地依赖项的代码和资源与应用程序的每个版本相匹配。

// Specifies two flavor dimensions.
flavorDimensions "tier", "minApi"

productFlavors {
     free {
      // Assigns this product flavor to the "tier" flavor dimension. Specifying
      // this property is optional if you are using only one dimension.
      dimension "tier"
      ...
    }

    paid {
      dimension "tier"
      ...
    }

    minApi23 {
        dimension "minApi"
        ...
    }

    minApi18 {
        dimension "minApi"
        ...
    }
}

解决与依赖项匹配相关的生成错误

请考虑您的应用程序是否配置了称为 “暂存” 的生成类型, 但它的一个库依赖项不。当插件尝试构建应用程序的 “暂存” 版本时, 它将不知道要使用哪个版本的库, 并且您会看到类似于以下内容的错误消息:

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

该插件包括 DSL 元素, 帮助您控制 Gradle 应该如何解决应用程序和依赖项之间的直接变体匹配的情况。请参考下表, 以确定应使用哪种 DSL 属性来解决与 variant 识别依赖项匹配相关的某些生成错误。 输入图片说明 输入图片说明 输入图片说明

迁移本地模块的依赖项配置

使用具有变体意识的依赖项解决方案, 您不再需要使用特定于变体的配置 (如 freeDebugImplementation) 来进行本地模块依赖性-插件会为您处理此问题。

使用特定于变体的配置是可选的, 不会破坏您的构建。但是, 针对本地模块依赖项的特定变体 (例如, 使用配置: “debug”) 会导致以下生成错误:

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.

相反, 应按如下方式配置依赖项:

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'
}

注意: 尽管手动依赖关系的 Gradle API 仍然可用, 但建议您不要使用它。提供给项目 () DSL 的配置现在需要在构建类型和口味 (以及其他属性) 上与使用者相匹配。例如, 不可能通过这种机制使 “debug” 变体消耗 “释放” 变体, 因为生产者和消费者的变体不匹配。(在这种情况下, 名称 “debug” 指的是有关发布依赖项的部分中提到的已发布的配置对象。因为插件现在发布两个配置, 一个用于编译, 一个用于运行时, 这种选择一个配置的旧方法不再有效。

使用新的依赖项配置

Gradle 3.4 引入了新的 Java 库插件配置, 允许您控制是否将依赖项发布到使用该库的项目的编译和运行时路径。Android 插件正在采用这些新的依赖配置, 迁移大型项目来使用它们可以大大缩短构建时间。下表帮助您了解应该使用哪些配置。 输入图片说明

类似于以前版本的 Android 插件的依赖配置, 上述配置可用于特定口味或构建类型的依赖性。例如, 可以使用实现使依赖项可用于所有变体, 也可以使用 debugImplementation 使其仅用于模块的调试变体。

注意: 编译、提供和 apk 目前仍然可用。然而, 他们将被删除在下一个主要版本的 Android 插件

发布依赖

以下配置保存了库的可传递依赖性, 供其使用者使用:

  • variant_nameApiElements
  • variant_nameRuntimeElements 在以前版本的插件中, 曾经有一个单一的配置每个变种称为: variant_name。由于库现在可以控制其用户在编译时可访问的依赖项, 因此, 使用前一节中描述的新的依赖项配置, 现在有两种配置: 一个用于编译使用者, 另一个用于运行时。

要了解有关不同配置之间的关系的更多信息, 请转到 Java 库插件配置。

迁移自定义依赖项解决策略

该插件使用以下配置来解决变量的所有依赖项:

variant_nameCompileClasspath (_variant_nameCompile 不再工作) variant_nameRuntimeClasspath (_variant_nameApk 不再工作) 如果您仍在使用旧的配置, 将会出现类似于以下内容的生成错误:

Error:Configuration with old name _debugCompile found.
Use new name debugCompileClasspath instead.

在已解决的配置上设置解决策略的插件和生成文件需要适应新名称。由于新的生成模型延迟了依赖项解析, 因此现在可以在使用变体 API 时设置分辨率策略, 如下面的示例所示。(Android 插件现在包括了访问变量的配置对象的 getter。

// Previously, you had to apply a custom resolution strategy during the
// configuration phase, rather than in the execution phase. That's
// because, by the time the variant was created and the Variant API was
// called, the dependencies were already resolved.
// But now these configurations DO NOT WORK with the 3.0.0 Gradle plugin:
// configurations {
//     _debugCompile
//     _debugApk
// }
//
// configurations._debugCompile.resolutionStrategy {
//     ...
// }
//
// configurations.all {
//     resolutionStrategy {
//     ...
//     }
// }

// Instead, because the new build model delays dependency resolution, you
// should query and modify the resolution strategy using the Variant API:
android {
    applicationVariants.all { variant ->
        variant.getCompileConfiguration().resolutionStrategy {
            ...
        }
        variant.runtimeConfiguration.resolutionStrategy {
            ...
        }
        variant.getAnnotationProcessorConfiguration().resolutionStrategy {
            ...
        }
    }
}

从测试配置中排除应用程序依赖项

在以前版本的 Android 插件中, 您可以使用 “排除” 关键字从测试中排除应用程序的某些可传递依赖性。但是, 使用新的依赖项配置, 以下不再有效:

dependencies {
    implementation "com.jakewharton.threetenabp:threetenabp:1.0.5"
    // Note: You can still use the exclude keyword to omit certain artifacts of
    // dependencies you add only to your test configurations.
    androidTestImplementation("org.threeten:threetenbp:1.3.3") {
        exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    }
}

这是因为 androidTestImplementation 和 androidTestApi 扩展了模块的实现和 api 配置。即, 当 Gradle 解析配置时, 它们继承应用程序的实现和 api 依赖性。要从测试配置中排除某些应用程序依赖项, 必须在执行时使用变体 API 进行操作:

android.testVariants.all { variant ->
    variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}

API 更改

Android 插件3.0.0 引入 API 的改变, 删除某些功能, 并可能打破您现有的构建。该插件的最新版本可能会引入新的公共 api 来替换损坏的功能。

在生成时修改变量输出可能不起作用

使用变体 API 操作变体输出是用新插件中断的。它仍然适用于简单的任务, 如在构建时更改 APK 名称, 如下所示:

// If you use each() to iterate through the variant objects,
// you need to start using all(). That's because each() iterates
// through only the objects that already exist during configuration time—
// but those object don't exist at configuration time with the new model.
// However, all() adapts to the new model by picking up object as they are
// added during execution.
android.applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "${variant.name}-${variant.versionName}.apk"
    }
}

但是, 涉及访问文件对象的更复杂的任务不再起作用。这是因为在配置阶段中不再创建特定于变体的任务。这导致插件不知道其所有的输出前, 但它也意味着更快的配置时间。

manifestOutputFile 不再可用

processManifest () manifestOutputFile () 方法不再可用, 在调用它时会出现以下错误:

A problem occurred configuring project ':myapp'.
   Could not get unknown property 'manifestOutputFile' for task ':myapp:processDebugManifest'
   of type com.android.build.gradle.tasks.ProcessManifest.

您可以调用 processManifest. manifestOutputDirectory () 来返回包含所有生成的清单的目录的路径, 而不是调用 manifestOutputFile () 来获取每个变量的清单文件。然后, 您可以找到一个清单, 并将您的逻辑应用于它。下面的示例动态更改清单中的版本代码:

android.applicationVariants.all { variant ->
    variant.outputs.all { output ->
        output.processManifest.doLast {
            // Stores the path to the maifest.
            String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml"
            // Stores the contents of the manifest.
            def manifestContent = file(manifestPath).getText()
            // Changes the version code in the stored text.
            manifestContent = manifestContent.replace('android:versionCode="1"',
                    String.format('android:versionCode="%s"', generatedCode))
            // Overwrites the manifest with the new text.
            file(manifestPath).write(manifestContent)
        }
    }
}

配置 Wear 应用程序依赖性

为了支持针对 Android 应用的变型识别依赖性解决方案, 该插件现在将所有的图表结合起来, 然后再解决它们, 类似于它如何处理其他依赖关系。在以前的版本中, 插件单独解析 componentWearApp 依赖关系图。因此, 例如, 您以前可以执行类似下面的操作, 而 “自由” 变体将使用: wear2 和所有其他变体将使用: wear1:

dependencies {
    // This is the old way of configuring Wear App dependencies.
    wearApp project(':wear1')
    freeWearApp project(':wear2')
}

上面的配置不再适用于新插件。对于简单的项目, 没有超过一个磨损应用模块, 如果你的应用模块配置与你的主应用程序相同的变体, 你不再需要使用 flavorWearApp 配置。只需指定 wearApp 配置和主应用程序的每个变体都将使用 “磨损” 应用程序模块中的匹配变体:

dependencies {
    // If the main and Wear app modules have the same variants,
    // the following configuration uses automatic dependency matching.
    wearApp  project(':wearable')
}

如果你有多个磨损应用程序模块, 你想指定一个不同的磨损应用程序模块的每一个应用程序的味道, 你可以继续使用 flavorWearApp 配置如下 (但是, 你不能结合它与 wearApp 配置):

dependencies {
    paidWearApp project(':wear1')
    demoWearApp project(':wear1')
    freeWearApp project(':wear2')
}

使用批注处理器依赖项配置

在以前版本的插件中, 编译类路径上的依赖项被自动添加到处理器类路径中。即, 您可以将注释处理器添加到编译类路径中, 并按预期方式工作。但是, 通过向处理器添加大量不必要的依赖关系, 这将对性能产生重大影响。

使用 Android 插件3.0.0 时, 您必须使用 annotationProcessor 依赖项配置将标注处理器添加到处理器类路径中, 如下所示:

dependencies {
    ...
    annotationProcessor 'com.google.dagger:dagger-compiler:<version-number>'
}

如果该插件的 JAR 文件包含以下文件, 则假定依赖项是注释处理器: 元 INF/服务/javax. 处理. 处理器。如果插件在编译类路径上检测到标注处理器, 则生成将失败, 并会收到一条错误消息, 列出编译类路径上的每个标注处理程序。要修复该错误, 只需更改这些依赖项的配置即可使用 annotationProcessor。如果依赖项包括也需要在编译类路径上的组件, 请再次声明该依赖项并使用编译依赖项配置。

android-apt 插件用户: 这种行为改变目前并不影响 android 的插件。然而, 该插件将不兼容未来版本的 Android 插件的 Gradle。

禁用批注处理器错误检查

如果您对包含不需要的注释处理器的编译类路径具有依赖性, 则可以通过将以下内容添加到生成. gradle 文件中来禁用错误检查。请记住, 添加到编译类路径中的注释处理器仍未添加到处理器类路径中。

android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                includeCompileClasspath false
            }
        }
    }
}

如果您遇到迁移到新的依赖项解决策略的问题, 则可以通过设置 includeCompileClasspath true 将行为恢复为 Android 插件2.3.0。但是, 建议不要将行为还原到版本 2.3.0, 这样做的选项将在将来的更新中删除。为了帮助我们提高与您所使用的依赖关系的兼容性, 请提交一个 bug。

使用单独的测试模块

不同的测试模块现在是可识别的。这意味着不再需要指定 targetVariant。

测试模块中的每个变体都将尝试在目标项目中测试匹配的变体。默认情况下, 测试模块只包含一个调试变量, 但您可以创建新的生成类型和新的口味来创建新的变体以匹配已测试的应用程序项目。为每个变体创建一个 connectedCheck 任务。

要使测试模块只测试不同的生成类型, 而不是调试一个, 请使用 VariantFilter 在测试项目中禁用调试变体, 如下所示:

android {
    variantFilter { variant ->
        if (variant.buildType.name.equals('debug') {
            variant.setIgnore(true);
        }
    }
}

如果希望测试模块只针对某个应用程序的特定口味或生成类型, 则可以使用 matchingFallbacks 属性只针对要测试的变体。这还可以防止测试模块为自身配置这些变体。

库中的本地 jar 现在可传递

以前, 库模块将以非标准方式处理本地 jar 的依赖关系, 并将它们打包到 AAR 中。即使在多项目的建设, AAR 的消费者将看到这些 JAR 文件通过包装的版本。

Android 插件3.0.0 和更高的使用新的 Gradle api, 允许消费项目看到本地 jar 作为常规的可传递依赖性, 类似于基于 maven 坐标的依赖关系。为了适应新的 Gradle api, 插件改变了它处理本地 JAR 文件的几个方面。

项目间发布

库模块不再处理本地 jar。这是为了加速由对库模块代码的更改而导致的增量生成。 现在, 对库模块的转换只会影响项目范围。使用 PROJECT_LOCAL_DEPS 应用转换将失败, 因为此范围现在已被弃用。 对于其本地 jar 是外部流的一部分的 app 模块, PROJECT_LOCAL_DEPS 和 SUB_PROJECT_LOCAL_DEPS 流现在总是空的。 为本地库模块启用混淆不再影响库的代码。相反, 您应该在使用该库的 app 模块上运行混淆。 以前, 必须在库模块中解析库模块与其本地 JAR 依赖项之间的 Java 资源冲突。由于库模块不再处理本地 jar, 因此必须解决使用该库的应用程序模块中的冲突。 发布到 Maven 回购

在发布到 Maven 回购方面, 没有任何变化。将本地 jar 捆绑在一起, 并将其类资源合并到 AAR 的主 JAR 文件中。如果模块启用混淆, 则所有 jar 都将合并到主 jar 文件中。 使用 AAPT2 时的行为更改 为了改进增量资源处理, Android 插件3.0.0 在默认情况下启用 AAPT2。虽然 AAPT2 应立即处理较旧的项目, 但本节将介绍您应该注意的一些行为更改。

Android 清单中的元素层次结构

在早期版本的 AAPT 中, 嵌套在 Android 清单中不正确节点中的元素要么被忽略, 要么导致警告。例如, 请考虑以下示例:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.myname.myapplication">
   <application
       ...
       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
           <action android:name="android.intent.action.CUSTOM" />
       </activity>
   </application>
</manifest>

早期版本的 AAPT 只会忽略错位的

标记。但是, 使用 AAPT2, 您会得到以下错误:

AndroidManifest.xml:15: error: unknown element <action> found.

要解决此问题, 请确保您的清单元素嵌套正确。有关详细信息, 请阅读清单文件结构。

资源申报

不能再从 name 属性中指示资源的类型。例如, 下面的示例错误地声明了一个 attr 资源项:

<style name="foo" parent="bar">
    <item name="attr/my_attr">@color/pink</item>
</style>

以这种方式声明资源类型会导致以下生成错误:

Error: style attribute 'attr/attr/my_attr (aka my.package:attr/attr/my_attr)' not found.

要解决此错误, 请使用 type = “attr” 显式声明该类型:

<style name="foo" parent="bar">
  <item type="attr" name="my_attr">@color/pink</item>
</style>

此外, 在声明