跪拜 Guibai
← Back to the summary

AGP 9.0 Forces a New DSL, Built-in Kotlin, and Stricter R8 Rules

AGP 9.0 has been out for a while, but I haven't really looked into what's changed and how it differs from before. After all, it's a major version upgrade. So, in this article, let's take a look at AGP 9.0 together.

Version Compatibility Comparison

First, you need to know that if you want to upgrade AGP to 9.0, some of your other components must meet the following requirements:

DSL Classes Only Implement New Public Interfaces

This is a major change in AGP 9.0. In previous AGP versions, such as 7.x or 8.x, both new interfaces and the old DSL type BaseExtension were included. This approach is like server-side interfaces: every time new features are added, the old interfaces are retained for compatibility with older online versions. The same logic applies to AGP. The result was that we kept using the old DSL types, and few people used the new interfaces. So, in version 9.0, a kind of forced update operation was performed, requiring all developers who upgrade to 9.0 to use the new interfaces. At the same time, the old Variant API access will also change. The alternatives are as follows:

Deprecated API (8.x) Replacement (9.0)
applicationVariants, libraryVariants, testVariants, unitTestVariants androidComponents.onVariants
variantFilter androidComponents.beforeVariants
deviceProvider, testServer Gradle Managed Devices
Path access like sdkDirectory, ndkDirectory, bootClasspath androidComponents.sdkComponents
dexOptions No direct replacement (dx has been replaced by d8)
generatePureSplits Built-in distribution via Android App Bundle

Migration Requirements

When you decide to migrate your project's AGP to 9.0, you must pay attention to the following points:

  1. Ensure the project is compatible with built-in Kotlin (no longer use org.jetbrains.kotlin.android)
  2. KMP projects need to switch to the com.android.kotlin.multiplatform.library plugin
  3. All internal DSL references in custom build logic must be replaced with public DSL interfaces
  4. Third-party plugins must be upgraded to AGP 9.0 compatible versions

Opt-out Mechanism

Of course, whenever Google has a major update, it inevitably provides a switch for a smooth transition, for example, to handle compatibility issues caused by some third-party libraries using older AGP versions. The method is simple: set the newDsl switch to false in gradle.properties.

image.png

Of course, this convenience will be removed in AGP 10.0, which will completely eliminate the opt-out mechanism. This version is said to be released in mid-2026. Before that, in AGP 9.4, per-module opt-out will also be allowed by adding the following configuration in gradle.properties:

image.png

Built-in Kotlin Support

Another major update is that Kotlin support is built-in and enabled by default in 9.0. This means we no longer need to manually manage the KGP version in Gradle files. AGP 9.0 will automatically depend on KGP version 2.2.10 for you. However, if your project's KGP version is higher than 2.2.10, you still need to manually upgrade the built-in KGP or KSP version.

image.png

If there's an upgrade, there's also a downgrade. If the project's required KGP version is lower than the built-in version, we can only downgrade. However, before downgrading, one thing must be done: disable the built-in Kotlin feature. Otherwise, the default built-in version 2.2.10 will still be used. The way to disable it is also simple: add the following configuration in gradle.properties.

image.png

The downgrade method is slightly different from the upgrade and requires using a strict version.

image.png

However, this downgrade also has a bottom line: the minimum version that can be downgraded to is 2.0.0.

Default Value Changes

Below are the properties whose default values flipped from AGP 8.13 to 9.0. This allows you to evaluate which old version behaviors to retain during the upgrade.

Gradle Property 8.13 Default 9.0 Default Impact
android.newDsl false true New DSL enabled by default
android.builtInKotlin false true Built-in Kotlin enabled by default
android.uniquePackageNames false true All libraries must use unique package names
android.useAndroidx false true Mandatory use of AndroidX
android.default.androidx.test.runner false true Default use of AndroidJUnitRunner
android.dependency.useConstraints true false Affects dependency import optimization
android.enableAppCompileTimeRClass false true R class constants need refactoring (switch → if chain)
android.sdk.defaultTargetSdkToCompileSdkIfUnset false true When targetSdk is not set, it defaults to compileSdk
android.onlyEnableUnitTestForTheTestedBuildType false true Only enable unit tests for the tested build type
android.proguard.failOnMissingFiles false true Missing ProGuard files will cause an error
android.r8.optimizedResourceShrinking false true Resource shrinking optimized by default
android.r8.strictFullModeForKeepRules false true Keep rules are stricter (constructors must be explicitly kept)
android.defaults.buildfeatures.resvalues true false Only enable in subprojects that need resValues
android.defaults.buildfeatures.shaders true false Only enable in subprojects that need shaders
android.r8.proguardAndroidTxt.disallowed false true Only supports proguard-android-optimize.txt
android.r8.globalOptionsInConsumerRules.disallowed false true Prohibits global options in keep files when publishing libraries
android.sourceset.disallowProvider false true Must use Sources API to register generated source code
android.custom.shader.path.required false true Must configure glslc.dir

Removed Features

Feature 8.x Status 9.0
Wear OS Embedded App Support Available Removed (wearApp configuration deleted)
androidDependencies Task Available Removed
sourceSets Reporting Task Available Removed
Density Split APK Support Available Removed (Use App Bundle instead)

Removed DSL Items

Removed APIs

8.x API 9.0 Replacement
AndroidComponentsExtension.finalizeDSl finalizeDsl (case correction)
Component.transformClassesWith Instrumentation.transformClassesWith
Component.setAsmFramesComputationMode Instrumentation.setAsmFramesComputationMode
ComponentBuilder.enabled ComponentBuilder.enable
DependenciesInfoBuilder.includedInApk includedInApk
DependenciesInfoBuilder.includedInBundle includedInBundle
GeneratesApk.targetSdkVersion targetSdk
Variant.minSdkVersion / maxSdkVersion / targetSdkVersion minSdk / maxSdk / targetSdk
VariantBuilder.targetSdk or targetSdkPreview GeneratesApkBuilder.targetSdk or GeneratesApkBuilder.targetSdkPreview
VariantOutput.enable enabled
Removed VariantBuilder.unitTestEnabled Replaced by the more consistently named enableUnitTest on VariantBuilder subtypes that extend HasUnitTestBuilder
FeaturePlugin, FeatureExtension Deprecated and stopped
BaseExtension.registerTransform Deprecated and disabled

Removed Gradle Properties

Enforced Gradle Properties

DSL Change: CommonExtension Parameterization Removed

This is a source-level breaking change aimed at helping avoid major source-level changes in the future, but it also means that block methods need to be moved from CommonExtension to ApplicationExtension, LibraryExtension, DynamicFeatureExtension, and TestExtension.

AGP 8.13

image.png

AGP 9.0

image.png

R8 Changes

New Option: -processkotlinnullchecks

Configures how R8 handles Kotlin null checks:

Parameter Value Behavior
keep Make no changes
remove_message (default) Rewrite to call getClass(), keep null check but remove message
remove Completely remove null checks

Keep Rules No Longer Propagate to Companion Methods

Default SourceFile Change

Changed from SourceFile to r8-map-id-<MAP_ID>, supporting automatic stack trace deobfuscation in Logcat.

Synthetic Class Name Minimization

The j$ classes in DEX generated by L8 desugaring use short name formats (like $1), no longer containing long encoded names like $$ExternalSynthetic.

Removals

Finally

As expected of a major version update, it has changed significantly from previous versions. Personally, I feel that for most projects, upgrading to this version requires careful planning. This might be the most troublesome upgrade ever. However, from another perspective, once you cross this hurdle, the project's build scripts will become cleaner and more maintainable. So, better to endure short-term pain than long-term discomfort—get this done sooner rather than later.