赞
踩
大家都知道,每年Google Play都会要求所有上架海外的App在8月(新包)及11月(在架包)进行新系统版本的适配,今年,我们苦逼的海外开发者又必须要适配Android 14了,晚干不如早干,近期我们公司也是将Android 14适配提上了日程!
以下Android 14 实战所遇问题仅适用于我司项目,如果您所遇问题不同,欢迎留言或私信一起讨论。
我司目前项目环境如下:
- Java version :11.0.18
- Android version :API 33, Android 13
- Installation platform & version :Gradle 6.1.1
- AGP :4.0.1
首先,我们将compileSdkVersion以及targetSdkVersion升级为34,开始Build
- android {
- compileSdkVersion 34
-
- defaultConfig {
-
- minSdkVersion 23
- targetSdkVersion 34
-
- }
- }
不出意外,一堆报错。下面我们一一来看:
报错如下:
我们发现,在Android 34源码中,View的 onDraw 方法中,参数Canvas添加了注解NotNull :
- /**
- * Implement this to do your drawing.
- *
- * @param canvas the canvas on which the background will be drawn
- */
- protected void onDraw(@NonNull Canvas canvas) {
- }
如果项目中使用了kotlin并且覆写的onDraw 方法有空安全运算符 ?,导致了上面的报错
- override fun onDraw(canvas: Canvas?) {
- super.onDraw(canvas)
- }
修改方式也比较简单,我们去掉空安全运算符 ?即可
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- }
AAPT2 报错的具体原因在日志里面没有详细的信息的,在gradle 社区上找到如下回答,告知我这应该是Android 独有的问题,跟Gradle构建工具无关。
Google 搜索相关的问题,找到了如下的回答
以此为线索,我们Demo验证,创建新的项目将targetSdkVersion 升级到34,项目是可以正常打包的,但是引入了facebook-android-sdk后就会出现aapt2异常终止报错。按照上图所示去掉legacy-support-v4就又可以正常打包。
基于上述定位到的具体原因,我们傻瓜式的排查了项目所有的引用库,发现androidx.legacy:legacy-support-v4,androidx.core:core-splashscreen两个库会引发aapt2异常终止问题。移除这2个库重新构建项目就可以正常打包运行了。
但是,移除库还是会影响app的部分功能,例如splashscreen库(Android 12闪屏适配)。所以我们再次研究了Android 14适配的官方文档,该文档上提到了AGP 7.0.0以上的版本适配,虽然官方没有说适配14必须将AGP升到7.0+,但是我们抱着试试的态度进行了尝试环境配置如下。经过升级后项目可以正常打包了,也没有影响splashscreen库(Android 12闪屏适配)功能。适配后环境如下:
- Java version :11.0.18
- Android version :API 34, Android 14
- Installation platform & version :Gradle 7.5
- AGP :7.4.2
1.使用 Android Gradle 插件升级助理
google 官方文档 : 使用 Android Gradle 插件升级助理
打开Tools > AGP Upgrade Assistant ,随即出现的工具窗口中会显示默认升级的详细信息,其中包括项目的当前版本 AGP 以及此版本 Android Studio 所支持的AGP下拉列表,选择7.4.2版本后点击Run selected steps。
经过上面的步骤,项目的build.gradle文件以及gradle-wrapper.properties文件会发生变更如下
- gradle版本:7.5
- gradle plugin版本:7.4.2
- kotlin版本:1.6.21
2.修改项目build.gradle文件
修改前:
- buildscript {
- ext.kotlin_version = '1.6.21'
- repositories {
- maven {
- url "xxx"
- }
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.4.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
- ...
- }
- }
-
- allprojects {
- repositories {
- maven {
- url "xxx"
- }
- google()
- mavenCentral()
- }
- }
修改后:
- buildscript {
- ext.kotlin_version = '1.6.21'
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.4.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
- ...
- }
- }
-
- plugins {
- id 'com.android.application' version '7.4.2' apply false
- id 'com.android.library' version '7.4.2' apply false
- id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
- }
3.修改项目setting.gradle文件
修改前:
include ':app' ...
修改后:
- pluginManagement {
- repositories {
- gradlePluginPortal()
- google()
- mavenCentral()
- maven { url "xxx" //如果是http 需要配置allowInsecureProtocol = true }
- ...
- }
- }
-
- dependencyResolutionManagement {
- repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
- repositories {
- google()
- mavenCentral()
- maven { url "xxx" //如果是http 需要配置allowInsecureProtocol = true }
- ...
- }
- }
- include ':app' ...
4.修改各个module下的build.gradle文件
修改前:
- apply plugin: 'com.android.application'
- apply plugin: 'kotlin-android'
- apply plugin: 'kotlin-android-extensions'
- apply plugin: 'kotlin-kapt'
- ...
-
- android {
- compileSdkVersion xxx
- ...
- defaultConfig {
- minSdkVersion xxx
- targetSdkVersion xxx
- ...
- }
- repositories {
- flatDir {
- dirs 'libs'
- }
- }
- ...
- }
修改后:
- plugins{
- id 'com.android.application'
- id 'kotlin-android'
- id 'kotlin-android-extensions'
- id 'kotlin-kapt'
- ...
- }
- android {
- compileSdk xxx
- ...
- defaultConfig {
- minSdk xxx
- targetSdk xxx
- ...
- }
- //删除repositories flatDir
- ...
- }
-
- dependencies {
- implementation fileTree(dir: "libs", include: [".jar", ".aar"])
- ...
- }
改到这里,基本就大功告成了。下面简单的谈谈其他Android 14适配中需要注意的事项,仅供参考。
Tips:动态注册广播我想大家用到的可能性较高,大家可以重点关注下。
一.所有应用变更
类别 | 类型 | 名称 |
---|---|---|
无障碍 | 变更(所有应用) | 使用非线性字体缩放测试应用 由于 Android 支持字体放大高达 200%,因此您应执行界面测试,确保您的应用可以容纳更大的字体,而不会影响易用性。 |
核心功能 | 变更(所有应用) | 应用只能终止自己的后台进程 当您的应用调用 killBackgroundProcesses() 时,API 只能终止您自己应用的后台进程。 |
核心功能 | 变更(所有应用) | 系统在默认情况下会拒绝安排精确闹钟 对于以 Android 13 及更高版本为目标平台的大多数新安装应用,系统不再预先向其授予 SCHEDULE_EXACT_ALARM 权限,该权限默认处于拒绝状态。 |
核心功能 | 变更(所有应用) | 上下文注册的广播会在应用缓存期间加入队列 当上下文注册的广播已加入队列以传送给处于缓存状态的应用时,系统可能会将这些广播放入队列中。 |
安全 | 变更(所有应用) | 最低可安装目标 API 级别 用户无法安装 targetSdkVersion 低于 23 的应用。 |
安全 | 变更(所有应用) | 系统可能会隐去媒体所有者软件包名称 除非应用满足特定条件,否则系统会隐去 OWNER_PACKAGE_NAME 的值。 |
用户体验 | 变更(所有应用) | 授予对照片和视频的部分访问权限 当应用请求在 Android 13(API 级别 33)中引入的任何视觉媒体权限( READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO )时,用户可以授予其对照片和视频的部分访问权限。 |
用户体验 | 变更(所有应用) | 更新了全屏 intent 通知的权限要求 在 Android 14 中,只有提供通话和闹钟的应用才能使用 USE_FULL_SCREEN_INTENT 权限支持全屏 intent 通知。 |
用户体验 | 变更(所有应用) | 不可关闭的通知 如果您的应用向用户显示不可关闭的前台通知,请注意:Android 14 已更改此行为,允许用户关闭此类通知。 |
用户体验 | 变更(所有应用) | 数据安全信息 现在,您的应用的数据安全信息(例如数据共享做法)会出现在某些权限理由系统对话框和系统通知中。 |
适配指南:
1.Android14已无法安装 targetSdkVersion
低于 23
的应用
2.USE_FULL_SCREEN_INTENT权限在Android14仅限于提供通话和闹钟的应用,其他应用将被收回权限。
如果项目中有涉及,在升级Android14时,请检查权限:
可以使用新 API NotificationManager.canUseFullScreenIntent 检查应用是否具有该权限;如果没有,应用可以使用新 intent ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT 启动设置页面,在该页面中,用户可以授予权限。
3.如新项目中/新业务中涉及闹钟,精确闹钟权限需主动申请。从 Android 14 开始,系统不再向以 Android 13 及更高版本为目标平台的大多数新安装应用预先授予 SCHEDULE_EXACT_ALARM 权限,该权限默认处于拒绝状态。
4.android14中用户可以仅授予部分访问媒体权限。如果是使用照片选择器,或者此前应用已获取到访问权限,则不受影响。
如果是新安装在Android14设备中,请求细分权限如READ_MEDIA_IMAGES、
READ_MEDIA_VIDEO等,当弹框用户选择仅授予部分权限时,请求的细分权限只在应用存活期间生效,下次启动app仍需要请求权限,这可能会使用户感到惊讶,因此在申请细分权限时,加入READ_MEDIA_VISUAL_USER_SELECTED权限,这样当用户选择仅授予部分权限时,他会拒绝细分权限,而提供临时的访问权限,以便再次弹框请求细分权限。
二.以Android14为目标平台的应用
类别 | 类型 | 名称 |
---|---|---|
核心功能 | 变更(以 Android 14 及更高版本为目标平台的应用) | 必须提供前台服务类型 如果应用以 Android 14 为目标平台,则必须为应用中的每个前台服务指定至少一个前台服务类型。 |
核心功能 | 变更(以 Android 14 及更高版本为目标平台的应用) | OpenJDK 17 更新 在 OpenJDK 17 更新中,一些更改会影响应用兼容性,例如对正则表达式和 UUID 处理的更改。 |
限制非 SDK 接口 | 变更(以 Android 14 及更高版本为目标平台的应用) | 更新了非 SDK 接口限制 Android 14 包含更新后的受限制非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。 |
安全 | 变更(以 Android 14 及更高版本为目标平台的应用) | 对隐式 intent 和待处理 intent 的限制 对于以 Android 14 为目标平台的应用,Android 会限制应用向内部应用组件发送隐式 intent。 |
安全 | 变更(以 Android 14 及更高版本为目标平台的应用) | 运行时注册的广播接收器必须指定导出行为 以 Android 14 为目标平台且使用上下文注册的接收器的应用和服务必须指定一个标志,以指明接收器是否应导出到设备上的所有其他应用。 |
安全 | 变更(以 Android 14 及更高版本为目标平台的应用) | 更安全地动态加载代码 如果应用以 Android 14 为目标平台,并且使用动态代码加载 (DCL) 功能,则必须将所有动态加载的文件标记为只读。 |
安全 | 变更(以 Android 14 及更高版本为目标平台的应用) | Zip 路径遍历 对于以 Android 14 为目标平台的应用,Android 通过限制 Zip 文件条目名称所含的内容来防止 Zip 路径遍历漏洞。 |
安全 | 变更(以 Android 14 及更高版本为目标平台的应用) | 针对从后台启动 activity 的额外限制 对于以 Android 14 为目标平台的应用,如果应用想要在发送其他应用的 PendingIntent 或绑定该应用的服务时为自己的后台 activity 授予启动其他应用的特权,则必须选择启用。 |
适配指南:
1.前台Service必须在 <service> 指定 android:foregroundServiceType
属性,同时申请特定的相关权限,否则在调用 startForeground() 时引发 MissingForegroundServiceTypeException
。
关于特定权限,参考前台服务至少指定一项前台服务类型。
从趋势来看,service逐渐鸡肋,建议迁移使用 WorkManager 或用户发起的数据传输作业。
2、如果使用android默认jdk17,需要关注以下三点变化:
IllegalArgumentException
的新情况,因此请务必测试应用中使用正则表达式的情形。IllegalArgumentException
。Class.forName("java.lang.ClassValue")
是否会返回类更改运行时行为。如果您的应用是根据没有 java.lang.ClassValue
类的旧版运行时开发的,则这些优化可能会将 computeValue
方法从派生自 java.lang.ClassValue
的类中移除。3.在Android14中Intent遵循以下规则:
// Throws an exception when targeting Android 14. context.startActivity(new Intent("com.example.action.APP_ACTION"));
因此隐式Intent需指定 android:exported="true"或者调用Intent#setPackage(String)方法指定包名,否则将抛出异常
4.在Android14中,BroadcastReceiver的动态注册必须指定导出行为RECEIVER_EXPORTED
或 RECEIVER_NOT_EXPORTED
,如果只接受系统广播则不需要。
示例:context.registerReceiver(privateBroadcastReceiver, intentFilter, RECEIVER_NOT_EXPORTED);
5.动态加载jar包及其他代码文件时,需要首先设置只读模式,否则将抛出异常
示例:
File jar = new File("DYNAMICALLY_LOADED_FILE.jar"); try (FileOutputStream os = new FileOutputStream(jar)) { // Set the file to read-only first to prevent race conditions jar.setReadOnly(); // Then write the actual file content } catch (IOException e) { ... } PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);
6.在Android14中,如果 Zip 文件条目名称包含“..”或以“/”开头,ZipFile(String) 和 ZipInputStream.getNextEntry() 会抛出 ZipException。
7.针对后台启动activity增加了更多限制:
PendingIntent
时,如果它想要授予自己的后台 activity 启动待处理 intent 的启动特权,则必须选择启用。如需选择启用,应用应通过 setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED) 传递 ActivityOptions
软件包。bindService()
方法时包含 BIND_ALLOW_ACTIVITY_STARTS 标志。三.新功能和API
类别 | 类型 | 名称 |
---|---|---|
无障碍 | 新功能和 API | 将字体放大高达 200% 的非线性字体 Android 支持字体放大高达 200%,为弱视用户提供了符合《网络内容无障碍指南》(WCAG) 的其他无障碍选项。 |
核心功能 | 新功能和 API | OpenJDK 17 更新 Android 14 包含一些可进一步与 OpenJDK 17 LTS 版本保持一致的功能和改进,包括面向应用开发者和平台开发者的库更新和 Java 17 语言支持。 |
国际化 | 新功能和 API | 各应用语言偏好设定 Android 14 扩展了 Android 13(API 级别 33)中引入的按应用设定语言功能,并提供了一些额外的功能。 |
图形 | 新功能和 API | 路径现在可查询和插值 您可以查询路径以了解其内部内容,在结构完全匹配的路径中进行插值,并实现变形效果。 |
国际化 | 新功能和 API | 语法变化 API 借助语法变化 API,您可以更轻松地向具有语法性别的语言的用户提供支持,从而针对这些语言提供更个性化、更自然的用户体验。 |
国际化 | 新功能和 API | 地区偏好设置 当用户更改其地区偏好设置并在应用中镜像这些偏好设置时,应用可以接收通知。 |
用户体验 | 新功能和 API | Sharesheet 自定义操作和排名改进 Android 14 更新了系统 Sharesheet,以便为用户提供自定义应用操作和信息更丰富的预览结果。 |
用户体验 | 新功能和 API | 支持内置和自定义动画 使用新的系统返回 API 的应用可选择启用预测性返回,以自动接收应用内动画并支持自定义转换。 |
用户体验 | 新功能和 API | 针对应用商店的改进 Android 14 引入了多个新的 PackageInstaller API,可帮助应用商店改善其用户体验。 |
用户体验 | 新功能和 API | 屏幕截图检测 我们提供了一种可保护隐私的 API,如果用户在应用 activity 可见时截取屏幕截图,该 API 会调用回调并显示消息框消息。 |
本次Android出海实战,目前规划有以下文章,大家有迫切想看的或者有其他想法的,可以关注下面公众号并联系我们,感谢您的支持。
2.Android出海实战:Google Play 开发者账号申请(保姆级教程)
3.Android出海实战:Google Play 上架指引(保姆级教程)
4.Android出海实战:Duns码(邓白氏)申请
5.Android出海实战:Firebase FCM推送
出海之路,路远且艰。更多金融出海解决方案,欢迎关注公众号 趣浪出海 ,欢迎大家一起探讨更多合规问题,稳健航行世界之海。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。