Skip to content

Most visited

Recently visited

navigation

Migrate to Android Plugin for Gradle 3.0.0

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:

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.

Update Gradle version

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

Apply the plugin

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

Use variant-aware dependency management

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.

Declare flavor dimensions

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"
        ...
    }
}

Resolve build errors related to dependency matching

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 errorResolution

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 matchingFallbacks to specify alternative matches for a given build type, as shown below:

// In the app's build.gradle file.
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']
        }
    }
}

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 matchingFallbacks to specify alternative matches for the app's "free" product flavor, as shown below:

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

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 missingDimensionStrategy in the defaultConfig block to specify the default flavor the plugin should select from each missing dimension, as shown in the sample below. You can also override your selections in the productFlavors block, so each flavor can specify a different matching strategy for a missing dimension.

// 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 {}
    }
}

Migrate dependency configurations for local modules

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.

Use the new dependency configurations

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 or compile can result in significant build time improvements because it reduces the amount of projects that the build system needs to recompile. For example, if an implementation dependency changes its API, Gradle recompiles only that dependency and the modules that directly depend on it. Most app and test modules should use this configuration.

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.

Publish dependencies

The following configurations hold the transitive dependencies of a library for consumption by its consumers:

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.

Migrate custom dependency resolution strategies

The plugin uses the following configurations to resolve all the dependencies of a variant:

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 {
            ...
        }
    }
}

Exclude app dependencies from test configurations

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

API changes

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.

Modifying variant outputs at build time may not work

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.

manifestOutputFile is no longer available

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

Configure Wear app dependencies

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

Use the annotation processor dependency configuration

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.

Disable the annotation processor error check

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 setting includeCompileClasspath 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.

Use separate test modules

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.

Local JARs in libraries are now transitive

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

Publishing to Maven repo

Behavior changes when using AAPT2

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.

Element hierarchies in the Android manifest

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.

Declaration of resources

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

Android namespace with ForegroundLinearLayout

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.

Incorrect use of @ resource reference symbols

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

Known issues

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)