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:
- Gradle: Minimum version must be 9.1.0
- SDK build tools: Minimum version 36.0.0
- NDK: Corresponding version is 28.2.13676358
- Maximum supported API Level: Cannot exceed 36.1
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:
- Ensure the project is compatible with built-in Kotlin (no longer use
org.jetbrains.kotlin.android) - KMP projects need to switch to the
com.android.kotlin.multiplatform.libraryplugin - All internal DSL references in custom build logic must be replaced with public DSL interfaces
- 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.
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:
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.
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.
The downgrade method is slightly different from the upgrade and requires using a strict version.
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
AndroidSourceSet.jni(feature never worked properly)AndroidSourceSet.wearAppConfigurationNameBuildType.isRenderscriptDebuggable(feature never worked properly)DependencyVariantSelection→ Replaced byDependencySelection, which is exposed askotlin.android.localDependencySelectionInstallation.installOptions(String), replaced by the mutable property ofInstallation.installOptions- The experimental but never stabilized
PostProcessingblock ProductFlavor.setDimension→ Replaced by thedimensionpropertyLanguageSplitOptions, only useful for the deprecated Google Play Instant experienceDensitySplit, because the feature is no longer supported. The alternative is to use App Bundle
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
android.defaults.buildfeatures.aidl: ConfigurebuildFeatures { aidl = true }individually in each subproject insteadandroid.defaults.buildfeatures.renderscript: Configure individually in each subproject instead
Enforced Gradle Properties
android.r8.integratedResourceShrinking: Enforced, using it will cause an errorandroid.enableNewResourceShrinker.preciseShrinking: Enforced
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
AGP 9.0
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
- Before: Keep rules matching interface methods were automatically propagated to synthetic companion methods.
- 9.0: No longer propagates, affecting interface desugaring when
minSdk < 24.
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
-addconfigurationdebuggingsupport removed (using it will cause a compilation warning)D8Command.builder.setDesugaredLibraryKeepRuleConsumerremovedR8Command.Builder.setDesugaredLibraryKeepRuleConsumerremoved
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.