Sanniv Khare 7 min read

AGP 9.4 to 9.5: what to change in a Kotlin or RN Android build

AGP 9.4 to 9.5: what to change in a Kotlin or RN Android build

The short version: moving from Android Gradle plugin 9.4 to 9.5.0 (October 2026) changes almost nothing in your build files. The compatibility table is the same in both releases: Gradle 9.6.0 minimum, JDK 17, SDK Build Tools 36.0.0, NDK 28.2.13676358 by default, and API level 37 as the highest supported. If you are already on AGP 9.4 with Gradle 9.6, the hop is a one-line version change plus a check for the Configuration.setVisible deprecation. If you come from older pins, such as React Native 0.88.1’s AGP 9.2.1 with Gradle 9.4.1, the Gradle wrapper has to move first.

Checked on 8 Nov 2026 against the AGP 9.5.0 and AGP 9.4.0 release notes, the Gradle 9.6.0 release notes and the Android Dev Summit tools post of 28 Oct. We also read the build files inside the react-native 0.88.1 and 0.89.0-rc.0 npm packages, their community templates and @react-native/gradle-plugin 0.88.1, and Google’s android/nowinandroid at commit 2982e63.

Why we did this: BMDB on Android TV, Peepal and Shopkeeper are Kotlin projects on AGP 9.x, and our React Native banking app inherits the React Native template’s Gradle setup. The upgrade order is the same for all four, so we wrote it down once.

One correction before you start

Our own radar ticket for this post assumed AGP 9.5 raised the minimum Gradle version to 9.6.0. It didn’t. The 9.4.0 notes already list Gradle 9.6.0 as the minimum and default, with JDK 17, Build Tools 36.0.0 and NDK 28.2.13676358. Both pages say: “The maximum API level that Android Gradle plugin 9.x supports is API level 37.”

So if your project builds on 9.4 today, it already meets every 9.5 floor.

What the builds we checked pin today

Version pins in React Native 0.88.1, 0.89.0-rc.0, the RN template and nowinandroid

We read these from the npm tarballs and a shallow clone, not from a build:

  • React Native 0.88.1 and 0.89.0-rc.0 both pin agp = "9.2.1" in gradle/libs.versions.toml. 0.89.0-rc.0 moves targetSdk from 36 to 37 and the NDK from 27.1.12297006 to 28.2.13676358.
  • @react-native/gradle-plugin 0.88.1 also pins agp = "9.2.1". The template’s build.gradle asks for com.android.tools.build:gradle with no version, so the plugin’s pin is what an app gets.
  • The 0.88.1 template’s wrapper is Gradle 9.4.1, and the 0.89.0-rc.0 template’s wrapper is still 9.4.1. Both are below the 9.6.0 that AGP 9.4 and 9.5 require. The 0.88.1 template’s gradle.properties also sets android.newDsl=false and android.builtInKotlin=false.
  • nowinandroid pins AGP 9.3.2 with Gradle 9.7.1, compileSdk = 37 and targetSdk = 36.

For a React Native app, this means AGP 9.5 is an override you choose, not something React Native ships. If you set it, the Gradle wrapper must go to 9.6.0 or later in the same commit.

Upgrade steps

Upgrade order: Gradle 9.6, then AGP 9.5, then the R8 report
  1. Confirm JDK 17 or later for Gradle and Android Studio. The floor did not change. The 9.5 notes fix a warning in generateReleaseRFile “when using JDK 25” (issue 553121473), so JDK 25 is in use out there.
  2. Move the Gradle wrapper to 9.6.0 (or later). Code below.
  3. Bump AGP in your version catalog, or in the root buildscript for a React Native app. Code below.
  4. Keep compileSdk at 37 or below. That is the ceiling for both 9.4 and 9.5.
  5. Sync, build, then fix deprecation warnings (next section).
  6. Run the R8 Configuration Analyzer on the release variant.

Steps 2 and 3 in code

The wrapper command from the Gradle 9.6 notes:

./gradlew :wrapper --gradle-version=9.6.0 && ./gradlew :wrapper
# Before: gradle/wrapper/gradle-wrapper.properties (RN 0.88.1 template)
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
# After
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip

A plain Kotlin project with a version catalog:

# Before: gradle/libs.versions.toml
[versions]
agp = "9.4.1"
# After
[versions]
agp = "9.5.0"

A React Native app, where the root build.gradle takes AGP’s version from the RN Gradle plugin:

// android/build.gradle (React Native app)
dependencies {
    classpath("com.android.tools.build:gradle:9.5.0")   // was unversioned: RN pins 9.2.1
    classpath("com.facebook.react:react-native-gradle-plugin")
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
}

What changed in AGP 9.5

The 9.5.0 notes list no removed or renamed DSL entries and no changed gradle.properties defaults. What they do list:

Configuration.setVisible is deprecated

From the fixed issues (560282299): “The Configuration.setVisible(boolean) method has been deprecated”. Issue 565740572 adds that “Gradle 9.8.0 reports Configuration.setVisible() deprecations with AGP 9.4.1”. If a convention plugin or build.gradle.kts sets visibility on a configuration, delete the call:

// Before: build-logic convention plugin
configurations.create("lintChecksBundle") {
    isVisible = false
    isCanBeConsumed = false
}

// After: drop isVisible; keep the role flags
configurations.create("lintChecksBundle") {
    isCanBeConsumed = false
}

We grepped @react-native/gradle-plugin 0.88.1 and nowinandroid’s build-logic for isVisible = and setVisible(. There were no hits in either.

Fixes worth knowing about

  • 556343682: AGP 9.3.0’s optimization { enable = true } DSL “does not set isMinifyEnabled in the ApplicationVariant object”. This is fixed in 9.5. If you adopted the new DSL in 9.3 or 9.4 and have a plugin that reads isMinifyEnabled, recheck it after upgrading.
  • 532206373: l8DexDesugarLib rewrote the obfuscation mapping as a 25-byte stub. This is fixed in 9.5. If you ship core library desugaring with R8, compare your uploaded mapping.txt sizes before and after.
  • 550220873: a 9.2.0 regression where LibraryExtension.getSourceSets() caused a ClassCastException. This is fixed in 9.5.

Experimental: backup and restore tests

9.5 adds a testOptions.backupTests block behind two flags:

# gradle.properties
android.experimental.testSuiteSupport=true
android.experimental.enableBackupTest=true

Carried over from 9.4: two things that become mandatory in AGP 10

The 9.4.0 notes give advance warning of two AGP 10 changes:

  • “Starting with AGP 10, the new Variant API is mandatory for all projects.” For now, a module can opt out with android.newDsl.optOut=:module-name in gradle.properties. The React Native 0.88.1 template sets android.newDsl=false project-wide, so RN apps are the ones to watch when AGP 10 lands.
  • Dynamic feature modules must match the app’s variants 1:1. In 9.4 this is a warning, and android.enforceDynamicFeatureVariantMatching=true makes it an error. “From AGP 10.0 onwards, strict 1:1 variant parity will become enforced by default.” Turning the flag on now shows you the mismatches early.

Gradle 9.6: the parent-project lookup warning

Gradle 9.6.0 (released 18 June 2026) deprecates implicit property and method lookup in parent projects. Its notes say it “now emits a warning and will be removed in Gradle 10”. The warning also covers findProperty(), property() and hasProperty() when the value is resolved from a parent project. To fail early instead, enable the NO_IMPLICIT_LOOKUP_IN_PARENT_PROJECTS feature preview in settings.gradle(.kts).

This matters for React Native. In @react-native/gradle-plugin 0.88.1 we counted 29 calls to hasProperty(, findProperty( or .property(" in the Kotlin sources. Four of them read from project.rootProject explicitly, and the rest read from the module project, for flags like newArchEnabled, hermesEnabled and reactNativeArchitectures. Whether those produce the new warning depends on where your app defines the property. We have not run a build to see. This is an inference from the two pages, not a result.

The R8 Configuration Analyzer

The Android Dev Summit tools post says the analyzer is “now built into AGP 9.3”. It “scores your R8 configuration and ranks your keep rules by how much they cost you”. Run it with:

./gradlew :app:analyzeReleaseR8Config

The 9.4 and 9.5 notes don’t mention it, so on 9.5 it behaves as described for 9.3. The blog gives no report format, and we have not run it on our apps yet.

How we checked, and what is not run

  • Release notes: AGP 9.5.0 and 9.4.0, Gradle 9.6.0, and the Android Dev Summit tools post, read on 8 Nov 2026. The Gradle 9.x upgrade guide blocks automated fetches, so its breaking-change list is not covered here.
  • Sources: npm pack of react-native 0.87.1, 0.88.1 and 0.89.0-rc.0, @react-native-community/template 0.88.1 and 0.89.0-rc.0-4f3db58, and @react-native/gradle-plugin 0.88.1. We diffed gradle/libs.versions.toml, the template’s build.gradle, gradle.properties and wrapper. We also ran a shallow git clone of android/nowinandroid (2982e63, 20 Oct 2026). All of this was in a Linux container.
  • Not run: no Gradle build, sync or R8 analysis, on our projects or anyone else’s. Running steps 2 to 6 on Peepal and on the RN banking app is our next step, and we will add build times and the analyzer’s findings here when we have them.

Need this built? Let’s talk.

We build and run apps for Mac, TV, mobile and the web, and write about what we learn.