![](https://img.haomeiwen.com/i7129895/a989d294e77fb098.png)
随着 Google I/O 2023 发布的 Android beta2 ,预计 Android 14 将在2023年第三季度发布,目前看整体需要适配的内容已经趋向稳定,那就根据官方文档简单做个适配要点总结吧。
如何做到最优雅的版本适配?那就是尽可能提高 minSdkVersion ,说服老板相信低版本用户无价值论,低版本用户更多是羊毛党~
本文主要从以下两方面解读Android 14适配:
-
Android 14适配-行为变更:所有应用
-
Android 14适配-行为变更:以 Android 14 为目标平台的应用
Android 14适配-行为变更:所有应用
SCHEDULE_EXACT_ALARM 权限
精确提醒(Exact alarms)用于用户有目的的通知,或用于需要在精确时间发生的操作情况,从 Android 14 开始,该 SCHEDULE_EXACT_ALARM
权限不再预先授予大多数新安装的针对 Android 13 及更高版本的应用,默认情况下该权限会被拒绝。
如果用户通过备份还原操作将应用数据传输到运行Android 14 的设备上,权限仍然会被拒绝。如果现有的应用已经拥有该权限,它将在设备升级到 Android 14 时预先授予。
而现在需要权限 SCHEDULE_EXACT_ALARM
才能通过以下 API 启动确切的提醒,否则将抛出 SecurityException
:
应用只能杀死自己的后台进程
从 Android 14 开始,应用调用 killBackgroundProcesses()
时,该 API 只能杀死自己应用的后台进程。
如果传入其他应用的包名,该方法对该应用的后台进程没有影响,Logcat中会出现如下信息:
Invalid packageName: com.example.anotherapp
应用不应该使用 killBackgroundProcesses()
API ,或以其他方式尝试影响其他应用的进程生命周期,即使是在较旧的操作系统版本上。
Android 旨在将缓存的应用保留在后台,并在系统需要内存时自动终止它们,如果应用出现不必要地杀死其他应用,它会降低系统性能并增加电池消耗,因为稍后需要完全重启这些应用,比恢复现有的缓存应用占用的资源要多得多。
最低可安装目标 API 级别
从 Android 14 开始,无法安装 targetSdkVersion 低于 23 的应用 ,要求应用必须满足这些最低目标 API 级别,这样可以提高用户的安全性和隐私性。
恶意软件通常以较旧的 API 级别为 target,以绕过较新 Android 版本中引入的安全和隐私保护,例如一些恶意软件应用使用 targetSdkVersion 22 的来避免受到 Android 6.0 的运行时权限模型的约束。
Android 14 的这一变化使恶意软件更难避开安全和隐私管理,尝试安装针对较低 API 级别的应用将导致安装失败,并在 Logcat 中显示以下消息:
INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7
在升级到 Android 14 的设备上,任何低于targetSdkVersion23 的应用都将保持安装状态,如果你需要针对较旧 API 级别的应用进行测试,请使用以下 ADB 命令:
adb install --bypass-low-target-sdk-block FILENAME.apk
改变用户体验不可关闭通知的方式
如果你的应用向用户显示不可关闭的前台通知,Android 14 已更改行为以允许用户关闭此类通知。
此次更改适用于通过 Notification.Builder#setOngoing(true)
或者 NotificationCompat.Builder#setOngoing(true)
来设置 Notification.FLAG_ONGOING_EVENT
从而阻止用户关闭前台通知的应用 的行为。
现在 FLAG_ONGOING_EVENT
的行为已经改变,用户实际上可以关闭此类通知。
在以下情况下,该类通知仍然不可关闭:
- 当手机被锁定时
- 如果用户选择清除所有通知操作(这有助于防止意外)
此外,新行为不适用于以下用例中的不可关闭通知:
- 使用
CallStyle
与真实通话相关的通知 - 使用
MediaStyle
创建的通知 - 设备策略控制器 (DPC) 和企业支持包
非线性字体缩放至 200%
从 Android 14 开始,系统支持高达 200% 的字体缩放,为弱视用户提供符合 Web 内容无障碍指南 (WCAG) 的额外无障碍选项。
如果已经使用缩放像素 (sp) 单位来定义文本大小,那么此次更改可能不会对应用产生重大影响。
Android 14适配-行为变更:以 Android 14 为目标平台的应用
前台服务类型
targetSdkVersion 34 的情况下,必须为应用内的每个前台服务(foreground-services) 指定至少一种前台服务类型。
前台服务类型是在 Android 10 引入的,通过 android:foregroundServiceType
可以指定 <service>
的服务类型,可供选择的前台服务类型有:
camera
connectedDevice
dataSync
health
location
mediaPlayback
mediaProjection
microphone
phoneCall
remoteMessaging
shortService
specialUse
systemExempted
例如:
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application ...>
<service
android:name=".MyMediaPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
</application>
</manifest>
如果你 App 中的用例与这些类型中的任何一种都不相关,那么建议还是将服务迁移成 WorkManager 或 jobs 。
隐式意图限制
对于面向 Android 14 的应用,Android 通过以下方式限制应用向内部应用组件发送隐式 intent:
隐式 intent 仅传递给导出的组件,应用必须使用明确的 intent 来交付给未导出的组件,或者将组件标记为已导出(exported) 。
如果应用创建一个 mutable pending intent ,但 intent 未指定组件或包,系统现在会抛出异常。
这些更改可防止恶意应用拦截只供给用内部组件使用的隐式 intent,例如:
<activity
android:name=".AppActivity"
android:exported="false">
<intent-filter>
<action android:name="com.example.action.APP_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
如果应用尝试使用隐式 intent 启动该 activity,则会抛出异常:
// Throws an exception when targeting Android 14.
context.startActivity(Intent("com.example.action.APP_ACTION"))
要启动未导出的 Activity,应用应改用显式 Intent:
// This makes the intent explicit.
val explicitIntent =
Intent("com.example.action.APP_ACTION")
explicitIntent.apply {
package = context.packageName
}
context.startActivity(explicitIntent)
运行时注册的广播接收器必须指定导出行为
以 Android 14 为目标,并使用 context-registered receivers (ContextCompat.registerReceiver
)应用和服务的需要指定一个标志,以指示接收器是否应导出到设备上的所有其他应用:分别为 RECEIVER_EXPORTED
或 RECEIVER_NOT_EXPORTED
。
val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
ContextCompat.RECEIVER_EXPORTED
} else {
ContextCompat.RECEIVER_NOT_EXPORTED
}
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
如果应用仅通过
Context#registerReceiver
方法为系统广播注册接收器时,那么它可以不在注册接收器时指定标志, 例如android.intent.action.AIRPLANE_MODE
。
更安全的动态代码加载
如果应用以 Android 14 为目标平台并使用动态代码加载 (DCL),则所有动态加载的文件都必须标记为只读,否则,系统会抛出异常。
我们建议应用尽可能避免动态加载代码,因为这样做会大大增加应用因代码注入或代码篡改而受到危害的风险。
如果必须动态加载代码,请使用以下方法将动态加载的文件(例如 DEX、JAR 或 APK 文件)在文件打开后和写入任何内容之前立即设置为只读:
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, parentClassLoader)
为防止现有动态加载文件抛出异常,我们建议可以尝试在应用中再次动态加载文件之前,删除并重新创建这些文件。
重新创建文件时,请按照前面的指导在写入时将文件标记为只读,或者将现有文件重新标记为只读,但在这种情况下,强烈建议首先验证文件的完整性(例如,通过根据可信值检查文件的签名),以帮助保护应用免受恶意操作。
Zip path traversal
对于针对 Android 14 的应用,Android 通过以下方式防止 Zip 路径遍历漏洞:如果 zip 文件条目名称包含 “..” 或以 “/” 开头,则 ZipFile(String)
和 ZipInputStream.getNextEntry()
会抛出一个 ZipException
。
应用可以通过调用 dalvik.system.ZipPathValidator.clearCallback()
选择退出验证。
Android其他版本适配
关于 Android 12 和 Android 13 功能和权限的变更,点击下方链接前查看:
网友评论