🚨 政策背景
自 Google Play 政策调整以来,应用必须在 2025 年 11 月 1 日前适配 Android 15(API 级别 35),否则将无法发布更新。本文结合实际 Flutter 项目,详解 Android 端升级适配流程与实战技巧,适用于 Flutter 3.24.x 版本开发者。
应用必须以 Android 15(API 级别 35) 或更高级别为目标平台
状态 您将无法发布应用更新(还剩 120 天)
发送日期 2025年7月2日
最后期限 2025年11月1日 - 延期已获批
为了向用户提供安全可靠的使用体验,Google Play 要求所有应用都必须符合目标 API 级别要求。
自 2025年11月1日起,如果您的目标 API 级别不是在最新的 Android 版本发布日期前 1 年内推出的,您将无法更新您的应用。
🛠️ 升级准备工作
1. 下载更新 Android Studio 版本
- 指定版本:
Android Studio Meerkat Feature Drop | 2024.3.2 Patch 1 May 28, 2025
- Android Studio 历史版本下载地址: developer.android.com/studio/arch…
- 注意 Java 版本依赖 Android Studio 自带版本
2. 打开 Android Studio 下载相关依赖
- 打开
File -> Settings -> Languages & Frameworks -> Android SDK
- SDK Platforms 勾选 Android 15.0
- SDK Tools 勾选相关依赖,注意勾选
Show Package Details
- 勾选完成后,点击 Apply,下载安装
由于各自项目不同,具体依赖项下载,提供截图,作为参考项:
🔧 项目代码配置实战
1. 修改 android/settings.gradle
确保插件版本、依赖源正确,并加入新版 AGP 和 Flutter 插件支持:
plugins {
id "com.android.application" version '8.6.0' apply false
id "org.jetbrains.kotlin.android" version "1.9.22" apply false
}
2. 修改 android/build.gradle
为解决 namespace 适配问题,加入自动设置 namespace 的逻辑,适配 AGP 8.x 版本的变动。
完整脚本略长,可参考附录中的完整版代码(支持自定义 namespace 或从 AndroidManifest.xml
自动提取)。
3. 修改 gradle-wrapper.properties
指定使用 Gradle 8.7:
distributionUrl=https://services.gradle.org/distributions/gradle-8.7-all.zip
4. 修改 android/app/build.gradle
重点关注配置项:
android {
compileSdk 35
ndkVersion "26.3.11579264"
defaultConfig {
...
minSdk = 23
targetSdkVersion 35
}
}
注意根据项目需要设置 productFlavors、签名信息、Proguard 等参数。
5. 添加 proguard-rules.pro
文件
为防止 R8 误删类,可加入以下规则:
-keep class * extends android.app.Service
-dontwarn com.google.android.gms.**
-dontwarn com.google.android.play.core.**
更多规则详见 StackOverflow 提供的 R8 混淆修复方案。
🔧 完整项目代码配置(仅供参考)
1. android/settings.gradle
文件修改
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.6.0' apply false
id "com.google.gms.google-services" version "4.3.15" apply false
id "com.google.firebase.crashlytics" version "2.8.1" apply false
id "org.jetbrains.kotlin.android" version "1.9.22" apply false
}
include ":app"
2. android/build.gradle
文件修改,主要适配 namespace 问题
allprojects {
repositories {
google()
mavenCentral()
}
}
subprojects(project -> {
project.afterEvaluate(subproject -> {
if (subproject.getPlugins().hasPlugin("com.android.library") || subproject.getPlugins().hasPlugin("com.android.application")) {
try {
Object android;
if (subproject.getPlugins().hasPlugin("com.android.library")) {
android = subproject.getExtensions().getByType(com.android.build.gradle.LibraryExtension.class);
} else {
android = subproject.getExtensions().getByType(com.android.build.gradle.AppExtension.class);
}
String namespace = (String) android.getClass().getMethod("getNamespace").invoke(android);
System.out.println("item " + subproject.getName() + " Current Namespace: " + namespace);
if (namespace == null || namespace.isEmpty()) {
String packageName = null;
if (subproject.getPlugins().hasPlugin("com.android.application")) {
packageName = (String) android.getClass().getMethod("getDefaultConfig")
.invoke(android)
.getClass()
.getMethod("getApplicationId")
.invoke(android.getClass().getMethod("getDefaultConfig").invoke(android));
}
if (packageName == null || packageName.isEmpty()) {
File manifestFile = (File) android.getClass()
.getMethod("getSourceSets")
.invoke(android)
.getClass()
.getMethod("getByName", String.class)
.invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main")
.getClass()
.getMethod("getManifest")
.invoke(android.getClass().getMethod("getSourceSets").invoke(android).getClass().getMethod("getByName", String.class).invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main"))
.getClass()
.getMethod("getSrcFile")
.invoke(android.getClass().getMethod("getSourceSets").invoke(android).getClass().getMethod("getByName", String.class).invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main").getClass().getMethod("getManifest").invoke(android.getClass().getMethod("getSourceSets").invoke(android).getClass().getMethod("getByName", String.class).invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main")));
if (manifestFile.exists()) {
String manifestText = new String(java.nio.file.Files.readAllBytes(manifestFile.toPath()));
java.util.regex.Pattern packagePattern = java.util.regex.Pattern.compile("package=\"([^\"]*)\"");
java.util.regex.Matcher matchResult = packagePattern.matcher(manifestText);
if (matchResult.find()) {
packageName = matchResult.group(1);
}
}
}
if (packageName == null || packageName.isEmpty()) {
packageName = !subproject.getGroup().toString().equals("unspecified")
? subproject.getGroup().toString()
: "com.auto.generated." + subproject.getName();
}
android.getClass().getMethod("setNamespace", String.class).invoke(android, packageName);
System.out.println("Namespace set: " + packageName + " (item: " + subproject.getName() + ")");
File manifestFile = (File) android.getClass()
.getMethod("getSourceSets")
.invoke(android)
.getClass()
.getMethod("getByName", String.class)
.invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main")
.getClass()
.getMethod("getManifest")
.invoke(android.getClass().getMethod("getSourceSets").invoke(android).getClass().getMethod("getByName", String.class).invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main"))
.getClass()
.getMethod("getSrcFile")
.invoke(android.getClass().getMethod("getSourceSets").invoke(android).getClass().getMethod("getByName", String.class).invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main").getClass().getMethod("getManifest").invoke(android.getClass().getMethod("getSourceSets").invoke(android).getClass().getMethod("getByName", String.class).invoke(android.getClass().getMethod("getSourceSets").invoke(android), "main")));
if (manifestFile.exists()) {
String manifestText = new String(java.nio.file.Files.readAllBytes(manifestFile.toPath()));
if (manifestText.contains("package=")) {
String updatedManifestText = manifestText.replaceAll("package=\"[^\"]*\"", "");
java.nio.file.Files.write(manifestFile.toPath(), updatedManifestText.getBytes());
System.out.println("AndroidManifest.xml del package attribute (item: " + subproject.getName() + ")");
} else {
System.out.println("AndroidManifest.xml not found package attribute (item: " + subproject.getName() + ")");
}
} else {
System.out.println("Not found AndroidManifest.xml file (item: " + subproject.getName() + ")");
}
}
} catch (Exception e) {
System.out.println("set item " + subproject.getName() + " namespace error: " + e.getMessage());
}
}
});
});
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
3. android/gradle/wrapper/gradle-wrapper.properties
文件修改
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-8.7-all.zip
4. android/app/build.gradle
文件修改
- 注意下面是我项目的配置,参考修改
- 主要是 compileSdk、ndkVersion、targetSdkVersion 修改
plugins {
id "com.android.application"
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
namespace "com.xxxxx"
compileSdk 35
ndkVersion "26.3.11579264"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
applicationId "com.xxxx"
minSdk = 23
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
flavorDimensions "default"
productFlavors {
prod {
dimension "default"
resValue "string", "app_name", "xxx"
}
dev {
dimension "default"
applicationIdSuffix ".test"
resValue "string", "app_name", "xxx"
}
uat {
dimension "default"
applicationIdSuffix ".uat"
resValue "string", "app_name", "xxx"
}
}
}
flutter {
source '../..'
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
}
5. 增加 android/app/proguard-rules.pro
文件
- 处理R8压缩混淆问题
- 示例参考配置,请根据自身实际项目修改
-keep class * extends android.app.Service
-dontwarn com.google.android.gms.auth.api.credentials.Credential$Builder
-dontwarn com.google.android.gms.auth.api.credentials.Credential
-dontwarn com.google.android.gms.auth.api.credentials.CredentialPickerConfig$Builder
-dontwarn com.google.android.gms.auth.api.credentials.CredentialPickerConfig
-dontwarn com.google.android.gms.auth.api.credentials.CredentialRequest$Builder
-dontwarn com.google.android.gms.auth.api.credentials.CredentialRequest
-dontwarn com.google.android.gms.auth.api.credentials.CredentialRequestResponse
-dontwarn com.google.android.gms.auth.api.credentials.Credentials
-dontwarn com.google.android.gms.auth.api.credentials.CredentialsClient
-dontwarn com.google.android.gms.auth.api.credentials.HintRequest$Builder
-dontwarn com.google.android.gms.auth.api.credentials.HintRequest
-dontwarn com.google.android.play.core.splitcompat.SplitCompatApplication
-dontwarn com.google.android.play.core.splitinstall.SplitInstallException
-dontwarn com.google.android.play.core.splitinstall.SplitInstallManager
-dontwarn com.google.android.play.core.splitinstall.SplitInstallManagerFactory
-dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest$Builder
-dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest
-dontwarn com.google.android.play.core.splitinstall.SplitInstallSessionState
-dontwarn com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
-dontwarn com.google.android.play.core.tasks.OnFailureListener
-dontwarn com.google.android.play.core.tasks.OnSuccessListener
-dontwarn com.google.android.play.core.tasks.Task
💻 实际开发环境参考
Mac 电脑相关环境
[✓] Flutter (Channel stable, 3.24.3, on macOS 15.3 24D60 darwin-arm64, locale zh-Hans-CN)
• Flutter version 3.24.3 on channel stable at /Users/xxx/development/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 2663184aa7 (10 个月前), 2024-09-11 16:27:48 -0500
• Engine revision 36335019a8
• Dart version 3.5.3
• DevTools version 2.37.3
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
• Android SDK at /Users/xxx/Library/Android/sdk
• Platform android-35, build-tools 35.0.1
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 21.0.6+-13368085-b895.109)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 16.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 16B40
• CocoaPods version 1.16.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2024.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 21.0.6+-13368085-b895.109)
[✓] VS Code (version 1.101.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.112.0
Windows 电脑相关环境
[√] Flutter (Channel stable, 3.24.3, on Microsoft Windows [版本 10.0.22631.5335], locale zh-CN)
• Flutter version 3.24.3 on channel stable at D:\SDK\flutter\3.24.3
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 2663184aa7 (10 months ago), 2024-09-11 16:27:48 -0500
• Engine revision 36335019a8
• Dart version 3.5.3
• DevTools version 2.37.3
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
• Android SDK at C:\Users\xxxx\AppData\Local\Android\sdk
• Platform android-35, build-tools 35.0.1
• Java binary at: D:\Program Files\Android\Android Studio\jbr\bin\java
• Java version OpenJDK Runtime Environment (build 21.0.6+-13368085-b895.109)
• All Android licenses accepted.
[√] Chrome - develop for the web
• Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe
[√] Android Studio (version 2024.3.2)
• Android Studio at D:\Program Files\Android\Android Studio
• Flutter plugin can be installed from:
https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 21.0.6+-13368085-b895.109)
🔍 常见问题与踩坑点
- ⚠️ Java JDK 建议统一使用 Android Studio 自带版本,避免环境差异
- ⚠️ 部分插件可能不兼容 AGP 8.6,请逐个排查升级
- ⚠️ namespace 问题一定要配置完整,AGP 8.x 强制要求所有 module 显式声明
namespace
- ⚠️ Proguard 配置要提前准备,避免发布后崩溃或丢类
📚 参考资料
- Flutter Gradle Plugin 变更说明
- Android 15 适配指南
- Flutter 项目 Android 15 升级实践
- AGP 升级助手
- Android 15 新特性解析
- R8 混淆配置问题
- AGP 8.4.0 版本说明
- Flutter Stripe 插件 Android 配置
✅ 总结
本次适配是一次涉及 AGP、Gradle、Flutter 构建体系的大版本升级,建议团队逐项排查并记录升级脚本,便于后续版本演进。
如本文对你有所帮助,欢迎点赞、评论交流,或者收藏方便后续查阅 🫡