The Android Plugin for Gradle 3.0.0 is a major upgrade that brings significant performance improvements to large multi-module projects. In order to bring about these improvements, there are some breaking changes in the plugin behavior, DSL, and APIs.
For most projects, after you update Gradle and apply the latest version of the Android plugin, you'll experience none or only a few of the build errors described on this page. If you do encounter a build error after updating the plugin, simply search this page for the error output or navigate to the related topic, and follow the instructions to resolve the issue. Also check out the following video for an overview of the migration steps.
After you migrate your project, you can benefit from the following performance improvements:
implementation
, api
, compileOnly
, and runtimeOnly
.minSdkVersion
to 20 or lower and use legacy multi-dex.For a more complete list of updates and changes, read the Android plugin 3.0.0 release notes.
To learn more about issues that are currently being addressed, see the Known issues.
Android plugin 3.0.0 requires Gradle version 4.1
or higher. If you're opening an existing project using Android Studio 3.0 or later, follow the prompts to automatically update an existing project to the compatible version of Gradle.
To update Gradle manually, edit the URL in gradle-wrapper.properties
to the following:
distributionUrl=\
https\://services.gradle.org/distributions/gradle-4.1-all.zip
If you're opening an existing project using Android Studio 3.0 or later, follow the prompts to automatically update your project to the latest version of the Android plugin. To manually update your project, include the maven repo and change the plugin version in your project-level build.gradle
file as follows:
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'
}
}
Note: For multi-module and composite builds, you may get a build error if the Android plugin is loaded more than once per build. To learn more, read the
Known issues.
Android plugin 3.0.0 and higher include a new dependency mechanism that automatically matches variants when consuming a library. This means an app's debug
variant automatically consumes a library's debug
variant, and so on. It also works when using flavors—an app's freeDebug
variant will consume a library's freeDebug
variant.
In order for the plugin to accurately match variants, you need to declare flavor dimensions for all product flavors and provide matching fallbacks for instances where a direct match is not possible.
The plugin now requires that all flavors belong to a named flavor dimension—even if you intend to use only a single dimension. Otherwise, you will get the following build error:
Error:All flavors must now belong to a named flavor dimension. The flavor 'flavor_name' is not assigned to a flavor dimension.
To resolve this error, you need to first declare one or more dimensions using the flavorDimensions
property. After that, assign each flavor to one of the dimensions you declared, as shown in the sample below. Because the plugin automatically matches dependencies for you, you should name your flavor dimensions carefully. Doing so gives you more control over which code and resources from your local dependencies are matched with each version of your app.
// 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"
...
}
}
Consider if your app configures a build type called "staging", but one of its library dependencies does not. When the plugin tries to build the "staging" version of your app, it won't know which version of the library to use, and you'll see an error message similar to the following:
Error:Failed to resolve: Could not resolve project :mylibrary. Required by: project :app
The plugin includes DSL elements to help you control how Gradle should resolve situations in which a direct variant match between an app and a dependency is not possible. Consult the table below to determine which DSL property you should use to resolve certain build errors related to variant-aware dependency matching.
Cause of build error | Resolution |
---|---|
Your app includes a build type that a library dependency does not. For example, your app includes a "staging" build type, but a dependency includes only a "debug" and "release" build type. Note that there is no issue when a library dependency includes a build type that your app does not. That's because the plugin simply never requests that build type from the dependency. | Use // In the app's build.gradle file. |
For a given flavor dimension that exists in both the app and its library dependency, your app includes flavors that the library does not. For example, both your app and its library dependencies include a "tier" flavor dimension. However, the "tier" dimension in the app includes "free" and "paid" flavors, but a dependency includes only "demo" and "paid" flavors for the same dimension. Note that, for a given flavor dimension that exists in both the app and its library dependencies, there is no issue when a library includes a product flavor that your app does not. That's because the plugin simply never requests that flavor from the dependency. | Use // In the app's build.gradle file. |
A library dependency includes a flavor dimension that your app does not. For example, a library dependency includes flavors for a "minApi" dimension, but your app includes flavors for only the "tier" dimension. So, when you want to build the "freeDebug" version of your app, the plugin doesn't know whether to use the "minApi23Debug" or "minApi18Debug" version of the dependency. Note that there is no issue when your app includes a flavor dimension that a library dependency does not. That's because the plugin matches flavors of only the dimensions that exist in the dependency. For example, if a dependency did not include a dimension for ABIs, the "freeX86Debug" version of your app would simply use the "freeDebug" version of the dependency. | Use // In the app's build.gradle file. |
With variant-aware dependency resolution, you no longer need to use variant-specific configurations, such as freeDebugImplementation
, for local module dependencies—the plugin takes care of this for you.
Using variant-specific configurations is optional and doesn't break your build. However, targeting a specific variant of a local module dependency (for example, using configuration: 'debug'
) causes the following build error:
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.
You should instead configure your dependencies as follows:
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' }
Note: Even though the Gradle API for manual dependency matching is still available, it's not recommended that you use it. The configuration provided to the project()
DSL now needs to match the consumer in build type and flavors (and other attributes). For instance, it is not possible to make a "debug" variant consume a "release" variant through this mechanism because the producer and consumer variants would not match. (In this case, the name "debug" refers to the published configuration object mentioned above in the section about publishing dependencies.) Because the plugin now publishes two configurations, one for compiling and one for runtime, this old way of selecting one configuration no longer works.
Gradle 3.4 introduced new Java Library plugin configurations that allow you to control whether a dependency is published to the compile and runtime classpaths of projects that consume that library. The Android plugin is adopting these new dependency configurations, and migrating large projects to use them can drastically reduce build times. The following table helps you understand which configurations you should use.
New configuration | Deprecated configuration | Behavior |
---|---|---|
implementation | compile | When your module configures an implementation dependency, it's letting Gradle know that the module does not want to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime. Using this dependency configuration instead of |
api | compile | When a module includes an api dependency, it's letting Gradle know that the module wants to transitively export that dependency to other modules, so that it's available to them at both runtime and compile time. This configuration behaves just like compile (which is now deprecated), and you should typically use this only in library modules. That's because, if an api dependency changes its external API, Gradle recompiles all modules that have access to that dependency at compile time. So, having a large number of api dependencies can significantly increase build times. Unless you want to expose a dependency's API to a separate test module, app modules should instead use implementation dependencies. |
compileOnly | provided | Gradle adds the dependency to the compilation classpath only (it is not added to the build output). This is useful when you're creating an Android library module and you need the dependency during compilation, but it's optional to have present at runtime. That is, if you use this configuration, then your library module must include a runtime condition to check whether the dependency is available, and then gracefully change its behavior so it can still function if it's not provided. This helps reduce the size of the final APK by not adding transient dependencies that aren't critical. This configuration behaves just like provided (which is now deprecated). |
runtimeOnly | apk | Gradle adds the dependency to the build output only, for use during runtime. That is, it is not added to the compile classpath. This configuration behaves just like apk (which is now deprecated). |
Similar to dependency configurations on previous versions of the Android plugin, the above configurations are available for flavor- or build-type-specific dependencies. For example, you can use implementation
to make the dependency available to all variants, or you can use debugImplementation
to make it available to only the debug
variant(s) of the module.
Note: compile
, provided
, and apk
are currently still available. However, they will be removed in the next major release of the Android plugin.
The following configurations hold the transitive dependencies of a library for consumption by its consumers:
variant_nameApiElements
variant_nameRuntimeElements
In previous versions of the plugin, there used to be a single configuration per variant called: variant_name
. Since a library can now control which dependencies its consumers have access to at compile time, using the new dependency configurations described in a previous section, there are now two configurations: one for compilation of the consumer(s) and one for runtime.
To learn more about the relationships between the different configurations, go to The Java Library plugin configurations.
The plugin uses the following configurations to resolve all the dependencies of a variant:
variant_nameCompileClasspath
(_variant_nameCompile
no longer works)variant_nameRuntimeClasspath
(_variant_nameApk
no longer works)If you're still using the old configurations, you'll get a build error similar to the following:
Error:Configuration with old name _debugCompile found. Use new name debugCompileClasspath instead.
Plugins and build files that set a resolution strategy on the resolved configuration need to adapt to the new name. Because the new build model delays dependency resolution, it is now possible to set the resolution strategy while using the Variant API, as shown in the example below. (The Android plugin now includes getters to access the configuration objects of a variant.)
// 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 {
...
}
}
}
On previous versions of the Android plugin, you could exclude certain transitive dependencies of your app from your tests using the exclude
keyword. However, with the new dependency configurations, the following no longer works:
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'
}
}
That's because androidTestImplementation
and androidTestApi
extend the module's implementation
and api
configurations. That is, they inherit the app's implementation
and api
dependencies when Gradle resolves the configurations. To exclude certain app dependencies from your test configurations, you must do it at execution time using the Variant API:
android.testVariants.all { variant ->
variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}
Android plugin 3.0.0 introduces API changes that removes certain functionalities and may break your existing builds. Later versions of the plugin may introduce new public APIs that replace broken functionalities.
Using the Variant API to manipulate variant outputs is broken with the new plugin. It still works for simple tasks, such as changing the APK name during build time, as shown below:
// 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" } }
However, more complicated tasks that involve accessing outputFile
objects no longer work. That's because variant-specific tasks are no longer created during the configuration stage. This results in the plugin not knowing all of its outputs up front, but it also means faster configuration times.
The processManifest.manifestOutputFile()
method is no longer available, and you get the following error when you call it:
A problem occurred configuring project ':myapp'. Could not get unknown property 'manifestOutputFile' for task ':myapp:processDebugManifest' of type com.android.build.gradle.tasks.ProcessManifest.
Instead of calling manifestOutputFile()
to get the manifest file for each variant, you can call processManifest.manifestOutputDirectory()
to return the path of the directory that contains all generated manifests. You can then locate a manifest and apply your logic to it. The sample below dynamically changes the version code in the manifest:
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) } } }
To support variant-aware dependency resolution for Android Wear apps, the plugin now combines all the graphs before resolving them, similarly to how it handles other dependencies. In previous versions, the plugin resolved componentWearApp
dependency graphs separately. So, for example, you previously could do something like the following, and the "free" variant(s) would use :wear2
and all other variants would use :wear1
:
dependencies {
// This is the old way of configuring Wear App dependencies.
wearApp project(':wear1')
freeWearApp project(':wear2')
}
The configuration above no longer works with the new plugin. For simple projects with no more than one Wear app module, if your Wear app module configures the same variants as your main app, you no longer need to use the flavorWearApp
configuration. Simply specify the wearApp
configuration and each variant of the main app will consume the matching variant from the Wear app module:
dependencies {
// If the main and Wear app modules have the same variants,
// the following configuration uses automatic dependency matching.
wearApp project(':wearable')
}
If you have multiple Wear app modules and you want to specify a different Wear app module per app flavor, you can keep using the flavorWearApp
configuration as follows (however, you can't combine it with the wearApp
configuration):
dependencies {
paidWearApp project(':wear1')
demoWearApp project(':wear1')
freeWearApp project(':wear2')
}
In previous versions of the plugin, dependencies on the compile classpath were automatically added to the processor classpath. That is, you could add an annotation processor to the compile classpath and it would work as expected. However, this causes a significant impact to performance by adding a large number of unnecessary dependencies to the processor.
When using the Android plugin 3.0.0, you must add annotation processors to the processor classpath using the annotationProcessor
dependency configuration, as shown below:
dependencies {
...
annotationProcessor 'com.google.dagger:dagger-compiler:<version-number>'
}
The plugin assumes a dependency is an annotation processor if its JAR file contains the following file: META-INF/services/javax.annotation.processing.Processor.
If the plugin detects annotation processors on the compile classpath, your build fails and you get an error message that lists each annotation processor on the compile classpath. To fix the error, simply change the configuration of those dependencies to use annotationProcessor
. If a dependency includes components that also need to be on the compile classpath, declare that dependency a second time and use the compile
dependency configuration.
android-apt plugin users: This behavior change currently does not affect the android-apt
plugin. However, the plugin will not be compatible with future versions of the Android plugin for Gradle.
If you have dependencies on the compile classpath that include annotation processors you don't need, you can disable the error check by adding the following to your build.gradle
file. Keep in mind, the annotation processors you add to the compile classpath are still not added to the processor classpath.
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath false
}
}
}
}
If you are experiencing issues migrating to the new dependency resolution strategy, you can restore behavior to that of Android plugin 2.3.0 by settingincludeCompileClasspath true
. However, restoring behavior to version 2.3.0 is not recommended, and the option to do so will be removed in a future update. To help us improve compatibility with the dependencies you're using, please file a bug.
Separate test modules are now variant-aware. This means that specifying targetVariant
is no longer necessary.
Each variant in the test module will attempt to test a matching variant in the target project. By default, test modules contain only a debug
variant, but you can create new build types and new flavors to create new variants to match the tested app project. A connectedCheck
task is created for each variant.
To make the test module test a different build type only, and not the debug one, use VariantFilter
to disable the debug
variant in the test project, as shown below:
android {
variantFilter { variant ->
if (variant.buildType.name.equals('debug') {
variant.setIgnore(true);
}
}
}
If you want a test module to target only certain flavors or build types of an app, you can use the matchingFallbacks
property to target only the variants you want to test. This also prevents the test module from having to configure those variants for itself.
Previously, library modules would handle dependencies on local JARs in a non- standard way and would package them inside their AAR. Even in a multi-project build, consumers of the AAR would see these JAR files through the packaged version.
Android plugin 3.0.0 and higher use new Gradle APIs to allow consuming projects to see local JARs as regular transitive dependencies, similar to maven coordinate based dependencies. To adapt to the new Gradle APIs, the plugin changes a few aspects of how it handles local JAR files.
Inter-project publishing
Library modules no longer process local JARs. This is to speed up incremental builds that are caused by changes to a library module's code.
Transforms on library modules now can affect only the PROJECT
scope. Applying transforms using PROJECT_LOCAL_DEPS
will fail, as this scope is now deprecated.
For app modules whose local JARs are a part of the EXTERNAL
stream, the PROJECT_LOCAL_DEPS
and SUB_PROJECT_LOCAL_DEPS
streams are now always empty.
Enabling ProGuard for local library modules no longer affects the library's code. Instead, you should run ProGuard on the app module that consumes the library.
Previously, Java resource conflicts between a library module and its local JAR dependencies had to be resolved in the library module. Because local JARs are no longer processed by the library module, you must resolve the conflicts in the app module that consumes the library.
Publishing to Maven repo
To improve incremental resource processing, Android plugin 3.0.0 enables AAPT2 by default. Although AAPT2 should immediately work with older projects, this section describes some behavior changes that you should be aware of.
In previous versions of AAPT, elements nested in incorrect nodes in the Android manifest are either ignored or result in a warning. For example, consider the following sample:
<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>
Previous versions of AAPT would simply ignore the misplaced <action>
tag. However, with AAPT2, you get the following error:
AndroidManifest.xml:15: error: unknown element <action> found.
To resolve the issue, make sure your manifest elements are nested correctly. For more information, read Manifest file structure.
You can no longer indicate the type of a resource from the name
attribute. For example, the following sample incorrectly declares an attr
resource item:
<style name="foo" parent="bar"> <item name="attr/my_attr">@color/pink</item> </style>
Declaring a resource type this way results in the following build error:
Error: style attribute 'attr/attr/my_attr (aka my.package:attr/attr/my_attr)' not found.
To resolve this error, explicitly declare the type using type="attr"
:
<style name="foo" parent="bar"> <item type="attr" name="my_attr">@color/pink</item> </style>
Additionally, when declaring a <style>
element, its parent must also be style resource type. Otherwise, you get an error similar to the following:
Error: (...) invalid resource type 'attr' for parent of style
ForegroundLinearLayout
includes three attributes: foregroundInsidePadding
, android:foreground
, and android:foregroundGravity
. Note that foregroundInsidePadding
is not included in the android
namespace, unlike the other two attributes.
In previous versions of AAPT, the compiler would silently ignore foregroundInsidePadding
attributes when you define it with the android
namespace. When using AAPT2, the compiler catches this early and throws the following build error:
Error: (...) resource android:attr/foregroundInsidePadding is private
To resolve this issue, simply replace android:foregroundInsidePadding
with foregroundInsidePadding
.
AAPT2 now throws build errors when you omit or incorrectly place resource reference symbols (@
). For example, consider if you omit the symbol when specifying a style attribute, as shown below:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> ... <!-- Note the missing '@' symbol when specifying the resource type. --> <item name="colorPrimary">color/colorPrimary</item> </style>
When building the module, AAPT2 now throws the following build error:
ERROR: expected color but got (raw string) color/colorPrimary
Additionally, consider if you incorrectly include the symbol when accessing a resource from the android
namespace, as shown below:
... <!-- When referencing resources from the 'android' namespace, omit the '@' symbol. --> <item name="@android:windowEnterAnimation"/>
When building the module, AAPT2 now throws the following build error:
Error: style attribute '@android:attr/windowEnterAnimation' not found
build.gradle
file, as shown below: // Additionally, make sure that you don't wrap this in a // subprojects block. buildscript { ... dependencies { classpath 'com.android.tools.build:gradle:3.0.0' } }To resolve this issue for composite builds, you additionally need to make sure that, for the main project and each included project that uses the Android plugin, the buildscript classpaths are identical. This also requires the order of the classpaths you add to the
buildscript
block to be identical. For example, consider the following classpath dependencies included in the build.gradle
file of the main project: buildscript { ... dependencies { classpath "com.android.tools.build:gradle:3.0.0" classpath "me.tatarka:gradle-retrolambda:3.7.0" } }Now consider the following
build.gradle
file for another project included in the composite build: buildscript { dependencies { // Note that the order of plugins differs from that // of the main project's build.gradle file. This results // in a build error because Gradle registers this as a // different classloader. classpath "me.tatarka:gradle-retrolambda:3.7.0" classpath "com.android.tools.build:gradle:3.0.0" } }Revisit this page to check if a new version of Gradle has fixed this issue.
Error:Execution failed for task ':app:packageInstantRunResourcesDebug'. com.google.common.util.concurrent.MoreExecutors.directExecutor()Ljava/util/concurrent/Executor;To get around this build error, add the following to the
dependencies
block of your project-level build.gradle
file: dependencies { classpath ('com.google.firebase:firebase-plugins:1.1.0') { exclude group: 'com.google.guava', module: 'guava-jdk5' } ... }For more information, see issue #63180002.
android-apt
plugin is no longer supported. You should switch to the built-in annotation processor support, which has been improved to handle resolving dependencies lazily.