跳到主要内容

Android 14 适配指引

随着移动操作系统的不断进化,Google Play 对应用的 targetSdkVersion 也有新的要求。

自2024年8月31日起,新应用和应用更新必须以 Android 14(API 级别 34)或更高版本为目标平台,才能提交到 Google Play。

另外如果需要更多时间来更新应用,可申请延期至2024年11月1日。如有需要,请关注 Google Play Console 的延期表单,一般可在应用管理页的 Inbox 中找到。

更多详情,请参见 Google Play 应用在目标 API 级别方面需满足的要求

本文旨在为希望将其项目 targetSdkVersion 升级到 34 的开发者提供 API 级别 34 的变更与影响面,同时还有对影响点的排查方法以及修改方案,将帮助您确保您的应用在 Android 14 上获得良好的体验。

注意

为避免编译报错的问题,需将 compileSdkVersiontargetSdkVersion 都升级到 34。

Android 14 行为变更影响点排查

下面列出了可能会影响游戏开发者的功能和行为变更,如需关注 Android 14 的所有变更,请查阅:https://developer.android.com/about/versions/14/summary

对隐式和待处理 intent 的限制

对于 targetSdkVersion=34 的应用,隐式 intent 的行为在 Android 14 上有以下限制:

  • 隐式 intent 只能传送到导出的组件。应用必须使用显式 intent 传送到未导出的组件,或将该组件标记为已导出。
  • 如果应用通过未指定 component 或 package 的 intent 创建可变 PendingIntent,系统会抛出异常。

排查方法

业务可以通过 解包 APK 产物,查看 AndroidManifest 中是否包含同时符合以下条件的组件声明:

  • Android 组件声明包含属性 android:exported="false"
  • Android 组件声明中包含自定义 actionintent-filter

如有同时符合上述条件的组件声明,则有可能存在隐式 intent 启动组件的行为,届时请联系 [diemliang(梁世宇)] 做进一步排查。

示例代码:

<service android:name="com.example.ExampleService"
android:exported="false">
<intent-filter>
<action android:name="com.example.EXAMPLE_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>

运行时注册的广播接收器必须指定导出标志

广播接收器通常用于监听设备状态变更如网络状态切换、手机电量变化等,是一个特别常用的功能。

对于 targetSdkVersion=34 的应用,在运行时通过 Context 注册的 BroadcastReceiver 必须在参数中指定其导出属性,否则会抛出异常。

排查方法

业务可以通过 解包 APK 产物,搜索 smali 文件中是否包含以下关键代码:

Landroid/content/Context;->registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;

如有,则表示该处逻辑涉及使用 Context 注册 BroadcastReceiver,届时请联系 [diemliang(梁世宇)] 并提供 smali 文件做进一步排查。

注意

使用 Unity 2020 以及更早版本的业务,请参见 Unity

压缩路径验证

对于 targetSdkVersion=34 的应用,如果 zip 文件条目名称包含 “..” 或以 “/” 开头,则 ZipFile(String)ZipInputStream.getNextEntry() 会抛出 ZipException

此举是为了防止 Zip 路径遍历漏洞。

排查方法

业务可以通过 解包 APK 产物,搜索 smali 文件中是否包含以下关键代码:

Ljava/util/zip/ZipFile;-><init>(Ljava/lang/String;)V
Ljava/util/zip/ZipInputStream;->getNextEntry()Ljava/util/zip/ZipEntry;

如有,则表示业务涉及使用 ZipFile, ZipInputStream,需要排查使用此接口的功能在 Android 14 运行是否正常。
使用上述接口进行解压的压缩包中,压缩文件的路径如果包含 ".." 或以 "/" 开头,建议修改压缩包的结构以及解压方式。届时请联系 [diemliang(梁世宇)] 做进一步排查。

每个 MediaProjection Capture Session 需要征得用户同意

对于 targetSdkVersion=34 的应用,您的应用必须在每次 Capture Session 之前征求用户同意。单次 Capture Session 只能是 MediaProjection#createVirtualDisplay 的单次调用,且每个 MediaProjection 实例只能使用一次。

该 API 为屏幕录制相关接口,常见于录制玩家精彩操作。

排查方法

业务可以通过 解包 APK 产物,搜索 smali 文件中是否包含以下关键代码:

Landroid/media/projection/MediaProjection;->createVirtualDisplay(
Landroid/media/projection/MediaProjectionManager;->createScreenCaptureIntent()

如有,则表示业务涉及使用 MediaProjection,需要排查使用此接口的功能在 Android 14 运行是否正常,重点关注单次运行内多次进入使用此接口的功能的场景是否正常。届时请联系 [diemliang(梁世宇)] 做进一步排查。

前台服务必须定义类型

在游戏中打开 Google 登录或 WebView 等内容时,游戏进程会进入后台状态并可能会被系统关闭。为了避免此问题,您可为游戏设置前台服务的权限。

对于使用前台服务的 targetSdkVersion=34 应用,应用必须为每项前台服务指定至少一个前台服务类型。您应该选择一个能够代表应用用例的前台服务类型。系统需要特定类型的前台服务满足特定用例。

您也需在 Google Play Console 的 应用内容 > 前台服务权限 内提交前台服务声明。例如​​,如果您的应用使用了 WebView 的功能,在页面选择 其他,然后添加简单说明以及应用在游戏中打开 WebView 的视频 URL。

Image: GoogleService

有关可选择的前台服务类型,请参见 前台服务类型是必需的

排查方法

业务可以通过 解包 APK 产物,搜索 smali 文件中是否包含以下关键代码,确定是否有使用前台服务:

->startForeground(ILandroid/app/Notification;)V
->startForeground(ILandroid/app/Notification;I)V

如有,则需要在 AndroidManifest.xml 中查看对应的 Service 定义,是否包含 android:foregroundServiceType 属性且类型合理。届时请联系 [diemliang(梁世宇)] 做进一步排查。

示例代码:

<!-- AndroidManifest.xml -->
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 添加对应的特定权限,此处示例为 mediaPlayback 所对应的权限:FOREGROUND_SERVICE_MEDIA_PLAYBACK -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application ...>
<!-- 在 service 中定义 foregroundServiceType -->
<service
android:name=".MyMediaPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
</application>
</manifest>

开发环境适配修改点

Unity 2020 以及更老的版本,UnityPlayerActivity#onCreate 会使用到不指定导出标记的 registerReceiver 方法,这会导致 Unity 2020 以及更老的版本在尝试升级 targetSdkVersion 到 34 后,在 Android 14 上无法正常运行。

建议将 Unity 升级至修复了该问题的 LTS 版本或以上:

  • 2021.3.33f1
  • 2022.3.13f1
  • 2023.1.20f1
  • 2023.3.0a11

更多详情,请参见在 Unity Issue Tracker 中提出的这封 错误报告

Image: MigrateToAndroid14/Unity2020deprecated

升级 Android Gradle Plugin (AGP)

根据谷歌官方文档 特定 Android API 级别所要求的最低工具版本 中的说明,当应用 targetSdkVersion=34 时,AGP 版本的最低要求为 8.1.1。

实际验证发现,使用 7.1.2 即可满足当前版本编译需求,无需升到跨度更高的 8.1.1。

  • 对于 Unity 2022.2 及更高版本,因其默认的 AGP 版本已符合要求,无需处理
  • 对于 Unity 2022.2 以下的版本,需要参考以下步骤升级 AGP

升级 Gradle Wrapper 版本

  1. 在 Editor 菜单 Unity > Settings > External Tools 中找到 Gradle installed with Unity 选项并取消勾选后,在下方 Gradle 路径中选择设备的 gradle-7.2-bin 的路径,如: Image: MigrateToAndroid14/GradleWrapper

  2. 修改环境变量,将环境变量 GRADLE_HOME 设置为 Gradle 7.2 版本的路径:

    # sh
    export GRADLE_HOME=/Users/***/.gradle/wrapper/dists/gradle-7.2-bin/****/gradle-7.2
    export PATH=${PATH}:${GRADLE_HOME}/bin

修改 AGP 版本依赖

注意

升级至 Player Network SDK V1.24 后,Assets/Plugins/Android/baseProjectTemplate.gradle 中已经将 AGP 升级到 7.1.2 版本,无需修改。

查看 Assets/Plugins/Android/baseProjectTemplate.gradle 文件中的 "com.android.tools.build:gradle:x.x.x",如果版本低于 7.1.2,需要修改至 7.1.2。否则无需修改。

Image: MigrateToAndroid14/AGP

升级 Java 版本

升级 targetSdkVersion 到 34 时,需要一并升级 Java 版本:

  • 对于 Unity 2022.3 及更高版本,因其默认的 Java 版本已符合要求,无需处理
  • 对于 Unity 2022.3 以下的版本,需要参考以下步骤升级 Java
  1. Assets/Plugins/Android/gradleTemplate.properties 中添加 org.gradle.java.home,value 为 Java 11 所在的路径,如:

    org.gradle.java.home=/Users/***/Library/Java/jdk-11.0.22.jdk/Contents/Home
  2. 修改环境变量,将环境变量 JAVA_HOME 设置为 Java 11 版本的路径:

    # sh
    # set JAVA_HOME to your jdk 11 installation path.
    export JAVA_HOME=/Users/***/Library/Java/jdk-11.0.22.jdk/Contents/Home
    export PATH=${PATH}:${JAVA_HOME}/bin

如何验收

基本信息确认

解包产物 APK,在包中的 AndroidManifest.xml 文件中,查看以下字段是否符合预期:

  • 查看 <manifest> 中的 package 属性,以确认产物包名是否正确;

  • 查看 <manifest> 中的 platformBuildVersionCode 属性,其值预期应为 34;

  • 查看 <uses-sdk> 中的 android:minSdkVersion 属性,其值应为业务所支持的最低 Android API 版本;

    Android API 与 Android 版本对照表:https://apilevels.com/

  • 查看 <uses-sdk> 中的 android:targetSdkVersion 属性,其值预期应为 34;

AndroidManifest.xml 示例:

<!---AndroidManifest-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:compileSdkVersion="34"
android:compileSdkVersionCodename="14"
package="your.package.name"
platformBuildVersionCode="34"
platformBuildVersionName="14">

<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="34" />
......

兼容性测试

用产物 APK 去进行兼容性测试,重点关注 APK 在 Android 14 设备上的表现,特别是与第二节的 Android 14 行为变更相关的场景;

附录

APK 解包指引

当业务需要确认游戏是否涉及一些变更的 API,建议是解 APK 包来全局地查找。解包工具推荐使用 apktool

apktool 使用示例:

apktool d your_apk_path.apk

执行上述命令后,解包结果会在 /your_apk_path 目录下:

Image: MigrateToAndroid14/ApktoolOutput-zh

FAQ

在 Unity Editor 中执行 **File > Build And Run** 报错

错误表现:

FAILURE: Build failed with an exception.
* Where:
Build file '/Users/***/launcher/build.gradle' line: 1
* What went wrong:
A problem occurred evaluating project ':launcher'.
> Failed to apply plugin 'com.android.internal.application'.
> The option 'android.enableR8' is deprecated.
It was removed in version 7.0 of the Android Gradle plugin.
Please remove it from `gradle.properties`.

enableR8 配置在 AGP 7.0 之后已经废弃,R8 默认为开启状态。

Edit > Project Settings > Player > Android > Publishing Settings > Minify 下方勾选 Use R8 即可使 Build And Run 正常。

Unity 2021 打包 mergeDex 阶段报错

错误表现:

> Task :launcher:mergeDexDebug FAILED
AGPBI: {"kind":"error","text":"com.android.tools.r8.kotlin.H","sources":[{}],"tool":"D8"}

com.android.tools.r8.kotlin.H

Execution failed for task ':launcher:mergeDexDebug'.
> Could not resolve all files for configuration ':launcher:debugRuntimeClasspath'.
> Failed to transform play-services-measurement-api-21.5.0.aar (com.google.android.gms:play-services-measurement-api:21.5.0) to match attributes {artifactType=android-dex, asm-transformed-variant=NONE, dexing-enable-desugaring=true, dexing-is-debuggable=true, dexing-min-sdk=16, org.gradle.status=release, org.gradle.usage=java-runtime}.
> Execution failed for DexingWithClasspathTransform: /Users/diemliang/.gradle/caches/transforms-3/e44e9aeb559794f90081ea4f47c131bc/transformed/jetified-play-services-measurement-api-21.5.0-runtime.jar.
> Error while dexing.

此问题为 Unity 2021 中指定了 r8 的版本为 4.0.48 导致的,该版本 r8 和 AGP 7.1.2 不兼容,谷歌官方文档中推荐与 7.1.2 AGP 搭配使用的 r8 版本为 3.1.51。

需要在 settingsTemplate.gradle 中修改 r8 的版本为 3.1.51:

dependencies {
// classpath("com.android.tools:r8:4.0.48")
classpath("com.android.tools:r8:3.1.51") // Change to 3.1.51
}

升级至 Player Network SDK V1.24 后,Assets/Plugins/Android/settingsTemplate.gradle 中已经将 r8 升级到 3.1.51 版本,无需修改。