Android14 适配之——targetSdkVersion 升级到 34 需要注意些什么?(下)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Android14 适配之——targetSdkVersion 升级到 34 需要注意些什么?(下)

1.2 蓝牙连接的权限变更


在 Android14 上,调用 BluetoothAdaptergetProfileConnectionState() API 时必须申请 BLUETOOTH_CONNECT  权限,以前不是必须的,现在必须在 Manifest 文件中声明,并且在运行时向用户申请该权限。

很明显 Android 这几年逐渐在回收一些系统权限,对于开发者来说更加麻烦了,但有利于广大的使用者。


1.3 OpenJDK 17 更新


Android14 继续更新 Android 的核心库,使其与最新的 OpenJDK LTS 版本的特性、功能保持一致,包括对库的更新以及对应用和平台开发人员的 Java17 语言的支持。以下的一些变化可能会影响应用的兼容性:

  1. 正则表达式的变更:有些正则表达式已经更改,及时检查应用中使用了正则表达式的地方,查看是否出错。可以在开发者选项中关闭兼容模式,方便将有问题的地方查找出来,具体的兼容模式开关在  系统 > 高级 > 开发者选项 > 应用兼容性变更 这里(原生系统在这里,其他厂商就不好说了),并在 list 中选中自己的 App 即可关闭或打开。在这里就是需要在 list 中滑到最底下的 Enabled for targetSdkVersion >= 34的地方,找到 DISALLOW_INVALID_GROUP_REFERENCE 选项切换;
  2. UUID 处理:在验证输入参数时,java.util.UUID.fromString() 方法会进行更严格的检查,因此可能会在反序列化时抛出 IllegalArgumentException 异常。自测方法同上,需要在 应用兼容性变更 下把 ENABLE_STRICT_VALIDATION 选项切换一下;
  3. ProGuard 出现的问题:在一些情况下使用 ProGuard 进行压缩,混淆,优化代码时,在添加了 java.lang.ClassValue 之后会出现问题。此问题是因为一个 Kotlin 库改变了运行时的行为,即在执行 Class.forName("java.lang.ClassValue") 是否会返回一个 class 而引发的,如果应用是针对没有 java.lang.ClassValue 的旧版本开发的,那么这些优化会从 java.lang.ClassValue 派生的类中删除 computeValue方法。

小结:JDK17 虽然会向下兼容,但有空还是升级一下比较好,毕竟有许多新的写法和优化。


2. 安全性


Android14 对安全性也有了更高的要求,这也是近几年来 Google 一直在关注的方向。


2.1 对隐式 Intent 和 PendingIntent 的限制


隐式 Intent(Implicit Intent)是 Android 应用程序组件之间进行通信的一种机制,它不明确指定要启动哪个组件,而是声明要执行的操作。系统会查找能够处理这个操作的组件,并启动它们。隐式 Intent 主要用于在应用程序内或与其他应用程序之间触发各种操作,如启动活动、启动服务、发送广播等。比较常见的例子就是先在 Manifest 文件中设置某个 Activity 中 intent-filter 的 action,然后可以通过设置启动 Intent 中的 action 来匹配这个 Activity 从而启动它.

  1. 隐式 Intent 只能传递给 android:exported="true" 的组件(四大组件:Activity、Service···)。所以在 App 中使用 Intent 传递数据要么使用显式 Intent 传递给 android:exported="false" 的组件;要么使用隐式 Intent 传递给 android:exported="true" 的组件。当然显式 Intent 传给 exported="true" 肯定也是可以的。
  2. 一个可变的 PendingIntent 必须设置 packageName,否则会抛出异常。


举个栗子:

// code 5
<!-- android:exported 设置为false,隐式 Intent 无法启动 -->
<activity
    android:name=".AppActivity"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
// code 6
// Throws an exception when targeting Android 14.  直接用隐式的 Intent 调用不管用
context.startActivity(Intent("com.example.action.SEND"))
// This makes the intent explicit. 当设置了 intent 中的 package 参数时就可以了
val explicitIntent =
        Intent("com.example.action.SEND")
explicitIntent.apply {
    `package` = context.packageName
}
context.startActivity(explicitIntent)

小结:这个变更得重视,特别是 android:exported 设置为 false 的组件,启动这些组件可能会崩溃,需要修改。这个更新还是为了安全,因为这些更改可以防止恶意应用拦截应用内部组件使用的隐式 Intent 。


2.2 动态广播接收器必须指定导出的行为


动态注册的广播接收器必须设置一个标记,用于表明接收器是否被导出到设备上的所有 App。标记位是 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED。早在 Android13 就引入了这个功能,可以让应用程序指定一个已注册的广播接收器是否应该被导出,并对设备上的其他应用可见。

只不过在 Android14 上变成了“必须设置”。而在以前的 Android 版本中,设备上的任何应用都可以向动态注册的广播接收器发送未受保护的广播,除非该接收器有签名许可。

举个栗子,在 A 应用中注册 AlarmReceiver 并发送广播:

// code 7
val filter = IntentFilter("alarmReceiver_custom_action")
val listenToBroadcastsFromOtherApps = true
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
    ContextCompat.RECEIVER_EXPORTED    // 该接收器对其他应用开放
} else {
    ContextCompat.RECEIVER_NOT_EXPORTED    // 该接收器不对其他应用开放
}
// 这里的 registerReceiver 方法必须设置 receiverFlags 参数
registerReceiver(requireContext(), AlarmReceiver(), filter, receiverFlags)
// 发送广播
val intent = Intent("alarmReceiver_custom_action")    // 方式1
//val intent = Intent(requireActivity(), AlarmReceiver::class.java)    // 方式2
requireActivity().sendBroadcast(intent)

在其他的应用中只能通过 code7 中的方式1发送广播,如果 A 应用的 listenToBroadcastsFromOtherApps 设置为 true,那么在 A 应用就能收到其他应用通过方式1发送的广播信息了,否则无法收到。

在实践中还发现,如果 A 应用也通过方式1发送自己应用内部的广播,且设置 ContextCompat.RECEIVER_NOT_EXPORTED,那么这个广播是无法收到的,感兴趣的同学可以试试。

如果应用程序只是通过 Context#registerReceiver 方法 (比如 Context#registerReceiver() )为系统广播注册接收器,那么它可以不在注册接收器时指定该标志。

小结:动态广播的注册方法改了,需要设置是否对其他应用可见,这跟 android:exported 的设置是一样的道理。其实本地广播和全局广播的功能和这个一样,只不过在 targetSdkVersion >= 34 上更加重视了。


2.3 更安全的动态代码加载


所有动态加载的文件都必须标记为只读。否则,系统将抛出异常。官方建议应用尽可能避免动态加载代码,因为这样做会大大增加应用被代码注入或代码篡改破坏的风险。

如必须动态加载代码,则需要将动态加载的文件(如 DEX、JAR 或 APK 文件)在文件打开并写入任何内容之前设置为只读:

// code 8
val jar = File("DYNAMICALLY_LOADED_FILE.jar")
val os = FileOutputStream(jar)
os.use {
    // Set the file to read-only first to prevent race conditions
    jar.setReadOnly()
    // Then write the actual file content
}
val cl = PathClassLoader(jar.absolutePath, parentClassLoader)

此外,为防止系统对现在已有的动态加载文件抛出异常,官方建议先删除并重新创建文件,然后再尝试在应用中重新动态加载这些文件。重新创建文件时,请按照上述指南在写入时将文件标记为只读。或者,可将现有文件重新标记为只读,但在这种情况下,官方建议先验证文件的完整性(例如,对照可信值检查文件的签名)以保护应用免遭恶意操作的影响。


2.4 Zip 路径遍历


针对 Android14 的应用,Android 系统通过以下方式防止 Zip 路径遍历的漏洞:如果 zip 文件条目名称包含 “..” 或以 “/” 开头,则 ZipFile(String)ZipInputStream.getNextEntry() 会抛出一个 ZipException 异常。

如果不想抛出异常且文件名称又不能改,可以通过调用 dalvik.system.ZipPathValidator.clearCallback() 选择退出验证。当然这是不推荐的。

Zip 路径遍历漏洞:指恶意攻击者通过构造含有 "../" 或以 "/" 开头的文件路径,在解压缩 Zip 文件时可以访问 Zip 文件之外的文件系统上的任意文件或目录,从而对应用程序造成安全风险的漏洞。


2.5 后台启动 Activity 新增限制


在 Android14 上系统进一步限制了 App 从后台启动 Activity 的情况:

  1. 当 App 使用 PendingIntent#send() 或类似方法发送 PendingIntent 时,必须选择是否要授予自己的后台 Activity 启动的权限来发送 PendingIntent 。如果选择授权,则需要通过 setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED) 方法返回并传递一个 ActivityOptions 对象。
  2. 当一个前台可见应用使用 bindService() 方法绑定另一个后台应用的 Service 时,这个可见应用现在必须选择是否将自己的后台 Activity 启动权限授予被绑定的服务。如果选择授权,应用在调用 bindService() 方法时需要设置 BIND_ALLOW_ACTIVITY_STARTS 标志。

这些变化扩展了现有的限制集,通过防止恶意应用程序滥用 API 从后台启动破坏性 Activity 来保护用户。

小结:针对后台启动控制得更严格了,如果项目中有相关逻辑,建议跑一跑看能否后台启动,如有问题再对照上面的内容进行修改,硬啃实在是不知道说的啥意思。。。


3. 有关限制非 SDK 接口的更新


Android14 更新了受限的非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试使用的 API 列表)。在限制使用非 SDK 接口之前,官方会尽可能确保有可用的公开替代方案。

如果应用并非以 Android14 为目标平台,其中一些变更可能不会立即对应用产生影响。但只要 App 使用任何非 SDK 方法或字段,终归存在导致应用出问题的显著风险。

一般而言,公共 SDK 接口是在 Android 框架 软件包索引(https://developer.android.google.cn/reference/packages) 中记录的那些接口。非 SDK 接口的处理是 API 抽象出来的实现细节,因此这些接口可能会在不另行通知的情况下随时发生更改。

如果不确定自己的应用是否使用了非 SDK 接口,则可以在 Debug 模式下运行测试 App,如果该应用访问了某些非 SDK 接口,系统就会输出一条日志消息。可以检查应用的日志消息,查找以下详细信息:

1)声明的类、名称和类型(采用 Android 运行时所使用的格式);

2)访问方式:链接、反射或 JNI;

3)所访问的非 SDK 接口属于哪个名单;

还可以使用 adb logcat 来查看这些日志消息,这些消息显示在所运行应用的 PID 下。举例而言,日志中可能包含如下条目:

Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)

如果应用依赖于非 SDK 接口,应该开始计划迁移到 SDK 的替代方案。如果无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应向官方请求新的公共 API。

如需查看 Android14 的所有非 SDK 接口的完整列表,可下载查看以下文件:hiddenapi-flags.csv(https://dl.google.…,这个表格文件内容很多,可用于查询。

小结:普通应用开发者一般情况下也不会用到非 SDK 接口,这个可忽略。

以上就是本篇的所有内容,可以看出,现有的 App 如果直接将 targetSdkVersion 升级到 34(Android14)的话还是有些地方需要注意并进行修改测试的。如果还想了解 Android14 新增了哪些功能,欢迎关注我,咱们下篇见!

更多内容,欢迎关注公众号:修之竹 或者查看 修之竹的 Android 专辑

赞人玫瑰,手留余香!欢迎点赞、转发~ 转发请注明出处~


参考文献


  1. Android 14 官方文档 https://developer.android.com/about/versions/14
  2. developer.android.google.cn/about/versi…
  3. Android 14 快速适配要点; 恋猫de小郭; https://juejin.cn/post/7231835495557890106?searchId=202307240025039D8229C74EA62159077B
  4. developer.android.google.cn/guide/compo…
  5. developer.android.com/about/versi…
  6. developer.android.google.cn/about/versi…
  7. developer.android.google.cn/about/versi…
  8. developer.android.google.cn/about/versi…
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
6月前
|
数据库 Android开发
Android 通过升级SettingsProvider数据强制覆盖用户的设置项
Android 通过升级SettingsProvider数据强制覆盖用户的设置项 【5月更文挑战第7天】
157 5
|
2月前
|
调度 Android开发 UED
Android经典实战之Android 14前台服务适配
本文介绍了在Android 14中适配前台服务的关键步骤与最佳实践,包括指定服务类型、请求权限、优化用户体验及使用WorkManager等。通过遵循这些指南,确保应用在新系统上顺畅运行并提升用户体验。
184 6
|
3月前
|
编解码 Android开发 UED
【性能狂飙!】揭秘Android应用极速变身秘籍:内存瘦身+用户体验升级,打造丝滑流畅新境界!
【8月更文挑战第12天】构建高效Android应用需全方位优化,尤其重视内存管理和用户体验。通过弱引用降低内存占用,懒加载资源减少启动负担。运用Kotlin协程确保UI流畅不阻塞,响应式设计适配多屏需求。这些策略共同提升了应用性能与用户满意度。
56 1
|
4月前
|
Dart API 开发工具
Flutter Android 14 强制升级说明 2024
猫哥我也是心大,当群友问我 flutter 如何升级编译 Android 14 时才发现需要提交新版本。
106 0
Flutter Android 14 强制升级说明 2024
|
4月前
|
IDE API Android开发
安卓与iOS开发环境的差异及适配策略
在移动应用开发的广阔舞台上,Android和iOS两大操作系统各据一方,各自拥有独特的开发环境和工具集。本文旨在深入探讨这两个平台在开发环境上的关键差异,并提供有效的适配策略,帮助开发者优化跨平台开发流程。通过比较Android的Java/Kotlin和iOS的Swift/Objective-C语言特性、IDE的选择、以及API和系统服务的访问方式,本文揭示了两个操作系统在开发实践中的主要分歧点,并提出了一套实用的适配方法,以期为移动开发者提供指导和启示。
|
3月前
|
安全 Java Android开发
Android 14适配Google play截止时间临近,适配注意点和经验
本文介绍了Android 14带来的关键更新,包括性能优化、定制化体验、多语言支持、多媒体与图形增强等功能。此外,还强调了适配时的重要事项,如targetSdkVersion升级、前台服务类型声明、蓝牙权限变更等,以及安全性与用户体验方面的改进。开发者需按官方指南更新应用,以充分利用新特性并确保兼容性和安全性。
263 0
|
5月前
|
存储 Linux 开发工具
Linux手动升级替换Android Studio
【6月更文挑战第22天】
125 8
|
5月前
|
Android开发
如何 将Android Studio升级至最新版(4.0)
如何 将Android Studio升级至最新版(4.0)
983 0
|
6月前
|
存储 缓存 Android开发
Android系统分区与升级
Android系统分区与升级
118 4