行为变更:所有应用
Android 17 平台包含一些可能会影响您的应用的行为变更。以下行为变更将影响在 Android 17 上运行的所有应用 ,无论采用哪种 targetSdkVersion 都不例外。您应该测试您的应用,然后根据需要酌情修改,以便支持这些变更。
此外,请务必查看仅影响以 Android 17 为目标平台的应用的行为变更列表。
注意 : 本页列出了一些比较重要的变更。如需了解更多详细信息,请参阅 Android 17 版本说明。
安全
Android 17 包含以下设备和应用安全方面的改进。
usesClearTraffic 弃用计划
我们计划在未来的版本中弃用 usesCleartextTraffic 元素。需要建立未加密 (HTTP) 连接的应用应迁移为使用网络安全配置文件,该文件可让您指定应用需要与哪些网域建立明文连接。
请注意,网络安全配置文件仅在 API 级别 24 及更高版本上受支持。如果应用的最低 API 级别低于 24,您应执行以下两项操作:
- 将
usesCleartextTraffic属性设置为true - 使用网络配置文件
如果应用的最低 API 级别为 24 或更高,您可以使用网络配置文件,而无需设置 usesCleartextTraffic。
限制隐式 URI 授权
目前,如果应用启动的 intent 包含具有操作 Send、SendMultiple 或 ImageCapture 的 URI,系统会自动向目标应用授予读取和写入 URI 权限。我们计划在 Android 18 中更改此行为。因此,我们建议应用明确授予相关的 URI 权限,而不是依赖系统来授予这些权限。
每个应用的密钥库限制
应用应避免在 Android 密钥库中创建过多的密钥,因为它是设备上所有应用的共享资源。从 Android 17 开始,系统会强制限制应用可拥有的密钥数量。对于以 Android 17 或更高版本为目标平台的非系统应用,此限制为 50,000 个键;对于所有其他应用,此限制为 200,000 个键。无论系统应用以哪个 API 级别为目标,其密钥数量上限均为 20 万个。
如果应用尝试创建超出限制的密钥,则创建会失败并显示 KeyStoreException。异常的消息字符串包含有关密钥限制的信息。如果应用针对异常调用 getNumericErrorCode(),则返回值取决于应用的目标 API 级别:
- 以 Android 17 或更高版本为目标平台的应用:
getNumericErrorCode()会返回新的ERROR_TOO_MANY_KEYS值。 - 所有其他应用:
getNumericErrorCode()返回ERROR_INCORRECT_USAGE。
用户体验和系统界面
Android 17 包含以下变更,旨在打造更一致、更直观的用户体验。
在旋转后恢复默认 IME 可见性
Beginning with Android 17, when the device's configuration changes (for example, through rotation), and this is not handled by the app itself, the previous IME visibility is not restored.
If your app undergoes a configuration change that it does not handle, and the app needs the keyboard to be visible after the change, you must explicitly request this. You can make this request in one of the following ways:
- Set the
android:windowSoftInputModeattribute tostateAlwaysVisible. - Programmatically request the soft keyboard in your activity's
onCreate()method, or add theonConfigurationChanged()method.
人工输入
Android 17 包含以下变更,这些变更会影响应用与键盘和触控板等人工输入设备的互动方式。
在指针捕获期间,触控板默认传递相对事件
从 Android 17 开始,如果应用使用 View.requestPointerCapture() 请求捕获指针,并且用户使用触控板,系统会识别用户触摸操作产生的指针移动和滚动手势,并以与捕获的鼠标产生的指针和滚轮移动相同的方式将这些信息报告给应用。在大多数情况下,这使得支持捕获鼠标的应用无需为触控板添加特殊的处理逻辑。如需了解详情,请参阅 View.POINTER_CAPTURE_MODE_RELATIVE 的文档。
以前,系统不会尝试识别触控板的手势,而是以类似于触摸屏触摸的格式将原始的绝对手指位置传递给应用。如果应用仍需要此绝对数据,则应改为使用 View.POINTER_CAPTURE_MODE_ABSOLUTE 调用新的 View.requestPointerCapture(int) 方法。
媒体
Android 17 针对媒体行为做了以下更改。
后台音频强化
行为变更:以 Android 17 或更高版本为目标平台的应用
与之前的版本一样,Android 17 包含一些可能会影响应用的行为变更。以下行为变更仅影响以 Android 17 或更高版本为目标平台的应用。如果您的应用以 Android 17 或更高版本为目标平台,则应根据情况修改应用,以支持这些行为。
无论应用的 targetSdkVersion 为何,都请务必查看对 Android 17 上运行的所有应用都有影响的行为变更列表。
注意 : 本页列出了一些比较重要的变更。如需了解更多详细信息,请参阅 Android 17 版本说明。
核心功能
Android 17 包含以下变更,这些变更会修改或扩展 Android 系统的各种核心功能。
MessageQueue 的新无锁实现
从 Android 17 开始,以 Android 17 或更高版本为目标平台的应用会收到 android.os.MessageQueue 的新无锁实现。新实现可提升性能并减少丢帧,但可能会破坏依赖于 MessageQueue 私有字段和方法的客户端。
如需了解详情(包括缓解策略),请参阅 MessageQueue 行为变更指南。
静态 final 字段现在不可修改
Apps running on Android 17 or higher that target Android 17 or higher cannot change static final fields. If an app attempts to change a static final field by using reflection, it will cause an IllegalAccessException. Attempting to modify one of these fields through JNI APIs (such as SetStaticLongField()) will cause the app to crash.
无障碍
Android 17 进行了以下变更,以改进无障碍功能。
复杂 IME 实体键盘输入的无障碍支持
This feature introduces new AccessibilityEvent and TextAttribute APIs to enhance screen reader spoken feedback for CJKV language input. CJKV IME apps can now signal whether a text conversion candidate has been selected during text composition. Apps with edit fields can specify text change types when sending text changed accessibility events. For example, apps can specify that a text change occurred during text composition, or that a text change resulted from a commit. Doing this enables accessibility services such as screen readers to deliver more precise feedback based on the nature of the text modification.
App adoption
-
IME Apps: When setting composing text in edit fields, IMEs can use
TextAttribute.Builder.setTextSuggestionSelected()to indicate whether a specific conversion candidate was selected. -
Apps with Edit Fields: Apps that maintain a custom
InputConnectioncan retrieve candidate selection data by callingTextAttribute.isTextSuggestionSelected(). These apps should then callAccessibilityEvent.setTextChangeTypes()when dispatchingTYPE_VIEW_TEXT_CHANGEDevents. Apps targeting Android 17 that use the standardTextViewwill have this feature enabled by default. (That is,TextViewwill handle retrieving data from the IME and setting text change types when sending events to accessibility services). -
Accessibility Services: Accessibility services that process
TYPE_VIEW_TEXT_CHANGEDevents can callAccessibilityEvent.getTextChangeTypes()to identify the nature of the modification and adjust their feedback strategies accordingly.
安全
Android 17 对设备和应用安全性进行了以下改进。
活动安全性
In Android 17, the platform continues its shift toward a "secure-by-default" architecture, introducing a suite of enhancements designed to mitigate high-severity exploits such as phishing, interaction hijacking, and confused deputy attacks. This update requires developers to explicitly opt in to new security standards to maintain app compatibility and user protection.
Key impacts for developers include:
- BAL hardening & improved opt-in: We are refining Background Activity Launch (BAL) restrictions by extending protections to IntentSender. Developers must migrate away from the legacy MODE_BACKGROUND_ACTIVITY_START_ALLOWED constant. Instead, you should adopt granular controls like MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE, which restricts activity starts to scenarios where the calling app is visible, significantly reducing the attack surface.
- Adoption tools: Developers should utilize strict mode and updated lint checks to identify legacy patterns and ensure readiness for future target SDK requirements.
本地主机保护
To improve platform security and user privacy, Android 17 introduces a new install-time permission, USE_LOOPBACK_INTERFACE. This change restricts cross-app and cross-profile communication over the loopback interface (for example, 127.0.0.1 or ::1), which was previously implicitly allowed with the INTERNET permission. For apps targeting Android 17 or higher, the following rules apply:
- Mutual consent required: cross-app and cross-profile communication is now blocked by default. For a connection to succeed, both the sending app and the receiving app must explicitly declare the
USE_LOOPBACK_INTERFACEpermission in their manifests. - Intra-app traffic exempt: Loopback communication within the same app (intra-app communication) remains unaffected and does not require this new permission.
- Target SDK behavior:
- App targets Android 17 or higher: The permission must be explicitly requested. If it is missing, socket operations (such as TCP connect or UDP send) fail, typically returning an
EPERM(operation not permitted) error. - App targets API level 36 or lower: The permission is treated as a split permission on
INTERNET. Apps targeting lower API levels are auto-granted this permission if they holdINTERNET.
- App targets Android 17 or higher: The permission must be explicitly requested. If it is missing, socket operations (such as TCP connect or UDP send) fail, typically returning an
- Compatibility warning: If a receiving app updates its target to Android 17 but fails to request this permission, incoming connections from other apps are be rejected, even if the sending app targets a lower API level.
默认启用 CT
如果应用以 Android 17 或更高版本为目标平台,则默认启用证书透明度 (CT)。(在 Android 16 上,CT 可用,但应用必须选择启用。)
更安全的原生 DCL - C
If your app targets Android 17 or higher, the Safer Dynamic Code Loading (DCL) protection introduced in Android 14 for DEX and JAR files now extends to native libraries.
All native files loaded using System.load() must be marked as read-only. Otherwise, the system throws UnsatisfiedLinkError.
We recommend that apps avoid dynamically loading code whenever possible, as doing so greatly increases the risk that an app can be compromised by code injection or code tampering.
设备规格
Android 17 包含以下变更,可改善各种尺寸和外形规格的设备上的用户体验。
平台 API 变更,用于忽略大屏设备 (sw>=600dp) 上的屏幕方向、尺寸调整能力和宽高比限制
We introduced Platform API changes in Android 16 to ignore orientation, aspect ratio, and resizability restrictions on large screens (sw >= 600dp) for apps targeting API level 36 or higher. Developers have the option to opt out of these changes with SDK 36, but this opt-out will no longer be available for apps that target Android 17 or higher.
For more information, see Restrictions on orientation and resizability are ignored.
后台音频强化
从 Android 17 开始,音频框架对后台音频互动(包括音频播放、音频焦点请求和音量更改 API)强制执行限制,以确保这些更改是由用户有意发起的。
如果应用开发者打算在没有可见 activity 的情况下控制音频,则应确保应用具有使用 "使用时"(WIU) 功能启动的前台服务(不是 SHORT_SERVICE 类型)。如果前台服务是在响应 MediaSessionEvent 时启动,或者在应用对用户可见时启动,则会获得 WIU 功能。
如果应用尝试在应用处于无效生命周期时调用音频 API,则音频播放和音量更改 API 会静默失败,而不会抛出异常或提供失败消息。音频焦点 API 失败,结果代码为 AUDIOFOCUS_REQUEST_FAILED。
引入这些限制的目的是减少意外的后台音频 bug 体验。部分示例如下:
- 如果应用在没有前台服务的情况下播放音频,则可能会被冻结。当应用最终解除冻结时,它会意外地恢复音频播放,这可能会在数小时后发生。
- 在没有前台服务的情况下播放音频的应用会面临各种运行时限制,从而导致音频播放效果不流畅。
- 播放与 activity 生命周期分离,这可能会导致播放会话或焦点事件泄漏,并且用户无法停止播放。
我们建议开发者测试其应用,并针对任何受到有意音频使用情形负面影响的行为变更提供反馈。 请使用此 Android 17 应用兼容性问题跟踪器报告任何问题。
确定受影响的后台音频使用情形
审核音频播放实现,并确定您的应用是否打算即使在条件性情况下也提供后台音频互动功能。
如果您的应用仅在显示用户可见的 activity 时(包括使用画中画 (PiP) 模式)打算播放音频或利用音频 API,则不会受到任何这些更改的影响。
如果您的应用提供 VOIP 功能(包括视频通话应用),那么它必须已经满足针对播放引入的要求(通常通过利用推荐的 telecom API),才能成功录制音频,因此不太可能受到影响。
如果您的应用打算在屏幕关闭或用户完全关闭您的 activity 时继续播放音频(这在音乐流式传输应用或播客应用中最为常见),则您的应用会被视为提供后台音频功能,并且必须满足新要求。
可能会受到影响的后台音频使用场景
如果您的应用不遵循在应用打开时或响应明确的用户触发器时继续音频互动的模式,则很可能会静默抑制应用的功能。
例如,如果您的应用响应 BOOT_COMPLETE 而启动前台服务并尝试与音频互动,则该应用会被抑制。
可减轻影响的最佳后台音频实践
-
使用 media3 Jetpack 库的 MediaSessionService 组件来管理后台音频播放。
如果您这样做,由于该库有助于管理播放生命周期,因此您的应用不太可能受到后台强化功能的影响。
-
如果您未使用 media3 库,则需要手动启动
mediaPlaybackFGS。如果可能会出现后台音频,请务必在应用处于前台时启动前台服务。例如,如果您的应用是视频流应用,通常仅在前台运行,但包含在屏幕关闭时继续播放的用户操作,那么当用户启动播放触发器时,您的应用仍应启动前台服务。
这样做可确保前台服务以 WIU 功能启动。
-
在持续时间不到 10 分钟的短暂故障期间,保持
mediaPlaybackFGS 处于有效状态。如果您的应用出现暂时性故障(例如因网络活动而导致缓冲出现问题),或者出现预期的暂时性中断(例如
AUDIOFOCUS_LOSS_TRANSIENT),则播放意图应继续。因此,您的 FGS 应保持有效状态。 -
在播放结束时停止前台服务,并且仅在用户明确恢复播放时才重新开始播放。
如果出现永久性结束播放信号(例如,内容已播放完毕且未自动播放、出现
AUDIOFOCUS_LOSS、来自 UMO 的暂停事件或媒体键事件)或无法恢复的故障,您的应用应停止音频互动、停止前台服务并结束媒体会话。所有这些操作都与用户对"完成"所需后台音频互动的概念相符。完成此操作后,您的应用将不再具备后台音频互动功能。随后,如果用户明确恢复播放(例如通过您的应用界面或通过通用媒体对象播放按钮),则启动音频播放的 intent 应返回,从而导致 FGS 重新启动。
-
使用 adb shell 命令测试音频播放行为。
在 Android 16 和 Android 17 上测试变更
从 Android 16 开始,该功能已在"警告"级别实现。这意味着应用可以使用 adb shell cmd audio set-enable-hardening 手动测试后台音频强化执行情况。
如需在搭载 Android 16 的设备上启用强制执行,请运行以下命令:
adb shell cmd audio set-enable-hardening 1
如需在搭载 Android 17 的设备上停用强制执行,请运行以下命令:
adb shell cmd audio set-enable-hardening 0
我们还建议使用 logcat 或 adb 命令 adb dumpsys audio 来确定应用是否因音频强化强制执行而遇到静默失败。如果确实如此,日志将包含一个以 AudioHardening 为前缀的条目,其中包含您的软件包名称。
了解具备使用时功能的服务
一般来说,前台服务 (FGS) 必须在应用位于前台时启动,以延长用户发起的运行时间。在某些特定情况下,应用可以在后台运行时启动前台服务。不过,这些前台服务通常不会被授予"使用期间"功能。
WIU 充当安全门,可防止从后台启动的 FGS 在用户可能不知道应用活动的情况下执行某些敏感行为。它会阻止应用访问位置信息、摄像头或麦克风等敏感数据,并且从 Android 17 开始,它还会阻止通常需要可见界面上下文的音频 API。
以下是方便实用的参考信息:
- 标准 FGS:在应用可见时启动或被授予后台活动启动功能的服务会被授予 WIU 访问权限。
- 后台启动的 FGS (BFSL):大多数不授予 WIU 访问权限。授予 WIU 的主要例外情况是涉及明确用户意图的互动,例如通知点击、微件互动或来自外部设备的媒体键事件。
- 系统启动 FGS:使用 system-server 委托(例如,通过使用 Telecom Jetpack 库)启动的 FGS 会被授予 WIU 访问权限。
如需了解详情,请参阅与从后台启动前台服务相关的限制。
受影响的音频 API 的完整列表
|-------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 音频功能 | 结果 | 受影响的 API |
| 音频播放 | 播放已静音 没有任何 API 提供异常或失败消息 | AudioTrack.write() (NDK) AAudioStream_write 面向 Android 的 OpenSL ES 任何管理播放的客户端媒体库(例如 media3、Exoplayer 和 Oboe)也可能会受到影响。 |
| 音频焦点请求 | 返回 AUDIOFOCUS_REQUEST_FAILED 对其他应用的音频播放没有影响,未获取焦点 | AudioManager.requestAudioFocus() |
| 音量和响铃模式 API | 对铃声模式或音量没有影响(方法调用会被静默忽略) 没有任何 API 提供异常或失败消息 | AudioManager.setStreamVolume() AudioManager.setStreamMute() AudioManager.adjustStreamVolume() AudioManager.adjustVolume() AudioManager.adjustSuggestedStreamVolume() AudioManager.setRingerMode() |
MessageQueue 行为变更指南
从 Android 17 开始,以 Android 17 或更高版本为目标平台的应用会收到 android.os.MessageQueue 的新无锁实现。新实现可提升性能并减少丢帧,但可能会破坏依赖于 MessageQueue 私有字段和方法的客户端。
Android 17 通过重写底层 MessageQueue 类,对 Looper 和 Handler 的工作方式进行了重大改进。自 Android 操作系统首次发布以来,MessageQueue 一直依赖于单个锁来管理主线程的任务队列。此设计经常导致锁争用;主线程可能会被后台线程阻塞,从而导致丢帧和界面卡顿。
减轻影响
如果您的应用或其依赖项依赖于运行时反射来查看 MessageQueue 的内部,则可能会受到此变更的影响。避免使用运行时反射来检查 MessageQueue。
在旧版实现中,开发者有时会访问私有字段(例如 MessageQueue.mMessages)来检查待处理的消息。借助新的无锁实现,内部数据结构已完全改变。为了保持二进制兼容性,Android 17 保留了 mMessages 字段,但在新实现中,无论队列中是否有消息,此字段始终为 null。
此外,如果您使用一些热门的测试库,则需要更新这些库,使其与新的 MessageQueue 实现兼容。
Espresso
Espresso 通常用于界面测试。Espresso 库需要知道主线程何时处于空闲状态,才能正确断言界面状态。旧版 Espresso 依赖于不再与无锁 MessageQueue 兼容的反射技术。
操作
更新到 Espresso 3.7.0 或更高版本。此版本使用 TestLooperManager API(尤其是 Android 16 引入的新 API)来安全地与 Looper 进行交互,而无需依赖内部实现细节。
Robolectric
同样,如果您使用 Robolectric 运行单元测试,并且测试依赖于旧版 Looper 模式,则可能会遇到问题。
操作
更新到 Robolectric 4.17 或更高版本。如果您使用的是 @LooperMode(LEGACY),则需要将测试迁移到新的 @LooperMode(PAUSED)。如需了解详情,请参阅 Robolectric 的迁移指南。
测试行为
您可以在 Android 17 上测试应用在启用此行为变更后的效果,而无需更新 targetSDK,只需执行以下命令:
adb am compat enable USE_NEW_MESSAGEQUEUE <your-package-name>
如果您的应用是可调试 build,此命令会启用应用中的无锁 MessageQueue。
如果您的应用以 Android 17 为目标平台,则默认启用新行为。如果您在以该 API 级别为目标平台后发现意外行为或崩溃,可以暂时停用新实现,以验证 MessageQueue 是否是导致问题的原因。
您可以使用以下两种方法中的任一种来切换更改:
-
开发者选项 中的应用兼容性变更菜单。
-
通过运行以下 ADB 命令:
adb am compat disable USE_NEW_MESSAGEQUEUE <your-package-name>
这会将您的应用恢复为基于锁的旧版实现,从而让您能够确定问题是否是由消息队列行为变更导致的。
系统会忽略有关屏幕方向和尺寸可调整性的限制
现在,Android 应用可在各种设备(例如手机、平板电脑、可折叠设备、桌面设备、汽车和电视)上运行,并且在大屏设备上支持多种窗口模式(例如分屏和桌面窗口),因此开发者应构建能够适应任何屏幕和窗口尺寸的 Android 应用,无论设备方向如何。在当今多设备的世界中,限制屏幕方向和尺寸可调整性等范式过于严格。
忽略屏幕方向、尺寸可调整性和宽高比限制
对于以 Android 17 或更高版本为目标平台的应用,屏幕方向、尺寸调整和宽高比限制不再适用于最小宽度大于 600dp 的显示屏。应用会填满整个显示窗口,无论宽高比或用户偏好的屏幕方向如何,都不会使用竖条模式。
Android 17 移除了 Android 16 中提供的针对大屏设备上的屏幕方向和尺寸调整限制的临时开发者选择停用功能。
此变更引入了新的标准平台行为。Android 正在向一种模式转变,在这种模式下,应用需要适应各种屏幕方向、显示大小和宽高比。固定屏幕方向或有限的尺寸调整等限制会阻碍应用的适应性。让您的应用具有自适应性,以提供尽可能最佳的用户体验。
您还可以使用应用兼容性框架并启用 UNIVERSAL_RESIZABLE_BY_DEFAULT 兼容性标志来测试此行为。
常见的重大更改
忽略屏幕方向、可调整大小性和宽高比限制可能会影响应用在某些设备上的界面,尤其是那些专为锁定为纵向的小布局设计的元素。例如,应用可能会出现布局拉伸、动画和组件超出屏幕等问题。您对宽高比或屏幕方向做出的任何假设都可能会导致应用出现视觉问题。详细了解如何避免这些问题并改进应用的自适应行为。
在横屏可折叠设备上或在多窗口、桌面窗口化或连接的显示屏等场景中进行宽高比计算时,常见的问题是相机预览画面出现拉伸、旋转或裁剪。此问题通常发生在大屏设备和可折叠设备上,因为应用假定相机功能(例如宽高比和传感器方向)与设备功能(例如设备屏幕方向和自然屏幕方向)之间存在固定关系。详细了解如何管理相机预览。
允许设备旋转会导致更多 activity 重新创建,如果未正确保留,可能会导致用户状态丢失。如需了解如何正确保存界面状态,请参阅保存界面状态。
实现细节
在全屏模式和多窗口模式下,以下清单属性和运行时 API 会被大屏设备忽略:
- screenOrientation
- resizableActivity
- minAspectRatio
- maxAspectRatio
- setRequestedOrientation()
- getRequestedOrientation()
系统会忽略 screenOrientation, setRequestedOrientation() 和 getRequestedOrientation() 的以下值:
portraitreversePortraitsensorPortraituserPortraitlandscapereverseLandscapesensorLandscapeuserLandscape
对于显示屏可调整大小性,android:resizeableActivity="false", android:minAspectRatio 和 android:maxAspectRatio 没有影响。
异常
在以下情况下,Android 17 的屏幕方向、尺寸可调整性和宽高比限制不适用:
- 游戏(基于 android:appCategory 标志)
- 用户在设备的宽高比设置中明确选择启用应用的默认行为
- 最小宽度小于
sw600dp的屏幕
功能和 API
Android 17 面向开发者引入了一些出色的新功能和 API。以下各部分总结了这些功能,可帮助您开始使用相关 API。
如需查看新增、修改和移除的 API 的详细列表,请参阅 API 差异报告。如需详细了解新的 API,请访问 Android API 参考文档,新 API 会突出显示以方便查看。
您还应查看平台变更可能会在哪些方面影响您的应用。如需了解详情,请参阅以下页面:
注意 : 此页面列出了一些比较重要的新功能。如需了解更多详细信息,请参阅 Android 17 版本说明。
核心功能
Android 17 添加了以下与核心 Android 功能相关的新功能。
新的 ProfilingManager 触发器
Android 17 向 ProfilingManager 添加了多个新的系统触发器,可帮助您收集深入的数据来调试性能问题。
新增的触发条件包括:
- TRIGGER_TYPE_COLD_START:触发器在应用冷启动期间触发。它会在响应中同时提供调用堆栈样本和系统轨迹。
- TRIGGER_TYPE_OOM:当应用抛出 OutOfMemoryError 并提供 Java 堆转储作为响应时,会触发此事件。
- TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE:当应用因 CPU 使用率异常过高而被终止时,系统会触发此事件,并提供调用堆栈样本作为响应。
如需了解如何设置系统触发器,请参阅有关基于触发器的分析以及如何检索和分析分析数据的文档。
JobDebugInfo API
Android 17 引入了新的 JobDebugInfo API,可帮助开发者调试 其 JobScheduler 作业,包括作业为何未运行、运行了多长时间以及 其他汇总信息。
扩展后的 JobDebugInfo API 的第一个方法是 getPendingJobReasonStats(),该方法会返回一个映射,其中包含作业处于 待执行状态的原因及其各自的累计待执行 时长。此方法将 getPendingJobReasonsHistory() 和 getPendingJobReasons() 方法结合在一起,让您能够了解预定 作业为何未按预期运行,但通过在单个方法中提供时长和作业原因,简化了信息检索。
例如,对于指定的 jobId,该方法可能会返回 PENDING_JOB_REASON_CONSTRAINT_CHARGING 和 60000 毫秒的时长,表明 作业因未满足充电限制而处于待执行状态 60000 毫秒。
隐私权
Android 17 包含以下新功能,可提升用户隐私保护。
Android 联系人选择器
Android 联系人选择器是一个标准化的可浏览界面,供用户与您的应用分享联系人。该选择器可在搭载 Android 17 或更高版本的设备上使用,可提供一种注重隐私保护的替代方案,以取代范围广泛的 READ_CONTACTS 权限。您的应用不会请求访问用户的整个地址簿,而是指定所需的数据字段(例如电话号码或电子邮件地址),然后用户选择要分享的特定联系人。这只会授予您的应用对所选数据的读取权限,从而确保精细控制,同时提供一致的用户体验,并具有内置的搜索、个人资料切换和多选功能,而无需构建或维护界面。
如需了解详情,请参阅联系人选择器文档。
安全
Android 17 新增了以下功能,以提升设备和应用安全性。
Android 高级保护模式 (AAPM)
Android 高级保护模式为 Android 用户提供了一套强大的新安全功能,标志着在保护用户(尤其是面临较高风险的用户)免遭复杂攻击方面迈出了重要一步。AAPM 是一项选择启用功能,只需进行一项配置设置即可激活。用户可以随时启用该功能,以应用一套主观的安全保护措施。
这些核心配置包括:禁止安装未知来源的应用(旁加载)、限制 USB 数据信号传输,以及强制执行 Google Play 保护机制扫描,从而显著减小设备的攻击面。 开发者可以使用 AdvancedProtectionManager API 与此功能集成,以检测模式的状态,从而使应用能够在用户选择启用此模式时自动采用强化型安全姿态或限制高风险功能。
连接
Android 17 添加了以下功能,以改进设备和应用连接。
受限卫星网络
实现优化,使应用能够在低带宽卫星网络上有效运行。
注意 : 此功能已随 Android 16 QPR2 季度版本正式发布。如需了解详情,请参阅针对受限卫星网络进行开发。
用户体验和系统界面
Android 17 包含以下变更,旨在提升用户体验。
接力
切换是 Android 17 中新增的一项功能和 API,应用开发者可以将其集成到应用中,以便为用户提供跨设备连续性。它允许用户在一个 Android 设备上启动应用 activity,然后将其转移到另一个 Android 设备。Handoff 在用户设备的后台运行,并通过各种入口点(例如接收设备上的启动器和任务栏)显示用户附近其他设备上的可用活动。
应用可以指定 Handoff 来启动相同的原生 Android 应用(如果该应用已安装在接收设备上且可供使用)。在此应用到应用流程中,用户通过深层链接跳转到指定 activity。或者,应用到网站切换功能可以作为后备选项提供,也可以通过网址切换功能直接实现。
切换支持是按 activity 实现的。如需启用 Handoff,请为 activity 调用 setHandoffEnabled() 方法。可能需要随切换传递额外数据,以便接收设备上重新创建的 activity 可以恢复适当的状态。实现 onHandoffActivityRequested() 回调以返回 HandoffActivityData 对象,该对象包含用于指定 Handoff 应如何处理并在接收设备上重新创建 activity 的详细信息。
实时更新 - 语义颜色 API
在 Android 17 中,实时更新启动了语义着色 API,以支持具有普遍意义的颜色。
以下类支持语义着色:
- Notification
- Notification.Metric
- Notification.ProgressStyle.Point
- Notification.ProgressStyle.Segment
填色游戏
- 绿色:与安全相关。 此颜色应在以下情况下使用:让别人知道您处于安全状态。
- 橙色:用于表示警告和标记物理危险。当用户需要注意设置更好的保护设置时,应使用此颜色。
- 红色:通常表示危险,停止。它应在需要紧急引起人们注意的情况下呈现。
- 蓝色:中性颜色,适用于信息性内容,应与其他内容区分开来。
以下示例展示了如何将语义样式应用于通知中的文本:
val ssb = SpannableStringBuilder()
.append("Colors: ")
.append("NONE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_UNSPECIFIED), 0)
.append(", ")
.append("INFO", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_INFO), 0)
.append(", ")
.append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
.append(", ")
.append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
.append(", ")
.append("DANGER", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_DANGER), 0)
Notification.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle("Hello World!")
.setContentText(ssb)
.setOngoing(true)
.setRequestPromotedOngoing(true)
适用于 Android 17 的 UWB 下行链路 TDoA API
下行链路到达时间差 (DL-TDoA) 测距技术可让设备通过测量信号的相对到达时间来确定其相对于多个锚点的位置。
以下代码段演示了如何初始化 Ranging Manager、验证设备功能并启动 DL-TDoA 会话:
class RangingApp {
fun initDlTdoa(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Register for device capabilities
val capabilitiesCallback = object : RangingManager.CapabilitiesCallback {
override fun onRangingCapabilities(capabilities: RangingCapabilities) {
// Make sure Dl-TDoA is supported before starting the session
if (capabilities.uwbCapabilities != null && capabilities.uwbCapabilities!!.isDlTdoaSupported) {
startDlTDoASession(context)
}
}
}
rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback)
}
fun startDlTDoASession(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Create session and configure parameters
val executor = Executors.newSingleThreadExecutor()
val rangingSession = rangingManager.createRangingSession(executor, RangingSessionCallback())
val rangingRoundIndexes = intArrayOf(0)
val config: ByteArray = byteArrayOf() // OOB config data
val params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes)
val rangingDevice = RangingDevice.Builder().build()
val rawTagDevice = RawRangingDevice.Builder()
.setRangingDevice(rangingDevice)
.setDlTdoaRangingParams(params)
.build()
val dtTagConfig = RawDtTagRangingConfig.Builder(rawTagDevice).build()
val preference = RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
.setSessionConfig(SessionConfig.Builder().build())
.build()
// Start the ranging session
rangingSession.start(preference)
}
}
private class RangingSessionCallback : RangingSession.Callback {
override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
// Process measurement results here
}
}
带外 (OOB) 配置
以下代码段提供了 Wi-Fi 和 BLE 的 DL-TDoA OOB 配置数据示例:
// Wifi Configuration
byte[] wifiConfig = {
(byte) 0xDD, (byte) 0x2D, (byte) 0x5A, (byte) 0x18, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
// BLE Configuration
byte[] bleConfig = {
(byte) 0x2D, (byte) 0x16, (byte) 0xF4, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
如果您无法使用 OOB 配置(因为缺少该配置),或者需要更改不在 OOB 配置中的默认值,则可以使用 DlTdoaRangingParams.Builder 构建参数,如以下代码段所示。您可以使用以下参数代替 DlTdoaRangingParams.createFromFiraConfigPacket():
val dlTdoaParams = DlTdoaRangingParams.Builder(1)
.setComplexChannel(UwbComplexChannel.Builder()
.setChannel(9).setPreambleIndex(10).build())
.setDeviceAddress(deviceAddress)
.setSessionKeyInfo(byteArrayOf(0x01, 0x02, 0x03, 0x04))
.setRangingIntervalMillis(240)
.setSlotDuration(UwbRangingParams.DURATION_2_MS)
.setSlotsPerRangingRound(20)
.setRangingRoundIndexes(byteArrayOf(0x01, 0x05))
.build()
联系人选择器
Android 联系人选择器是一个标准化的可浏览界面,供用户与您的应用分享联系人。该选择器可在搭载 Android 17 或更高版本的设备上使用,可提供一种注重隐私保护的替代方案,以取代范围广泛的 READ_CONTACTS 权限。您的应用不会请求访问用户的整个地址簿,而是指定所需的数据字段(例如电话号码或电子邮件地址),然后用户选择要分享的特定联系人。这只会授予您的应用对所选数据的读取权限,从而确保精细控制,同时提供一致的用户体验,并具有内置的搜索、个人资料切换和多选功能,而无需构建或维护界面。
集成联系人选择器
如需集成联系人选择器,请使用 Intent.ACTION_PICK_CONTACTS intent。此 intent 会启动选择器,并将所选联系人返回给您的应用。
与旧版 ACTION_PICK 不同,借助联系人选择器,您可以同时指定应用所需的多个数据字段。您可以使用 Intent.EXTRA_REQUESTED_DATA_FIELDS 来实现此目的,并传递 ContactsContract.CommonDataKinds 中定义的 MIME 类型的 ArrayList<String>。
常见的 MIME 类型包括:
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
启动选择器
使用 registerForActivityResult 和 StartActivityForResult 合约启动选择器。您可以配置 intent 以允许单选或多选。
选择单个联系人
在此示例中,应用仅请求电话号码。选择器将过滤列表,仅显示包含电话号码的联系人,并允许用户选择特定号码。
// Define the specific data fields you need
val requestedFields = arrayListOf(
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
// Set up the intent
val pickContactIntent = Intent(Intent.ACTION_PICK_CONTACTS).apply {
type = ContactsContract.Contacts.CONTENT_TYPE
putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS, requestedFields)
}
// Launch the picker
pickContactLauncher.launch(pickContactIntent)
选择多个联系人
如需启用多选,请添加 Intent.EXTRA_ALLOW_MULTIPLE extra。您可以选择性地限制用户可选择的商品数量。
val requestedFields = arrayListOf(
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
)
val pickMultipleIntent = Intent(Intent.ACTION_PICK_CONTACTS).apply {
type = ContactsContract.Contacts.CONTENT_TYPE
putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS, requestedFields)
// Enable multi-select
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
// Optional: Set a custom limit (max 50 recommended)
putExtra(Intent.EXTRA_SELECTION_LIMIT, 10)
}
pickMultipleLauncher.launch(pickMultipleIntent)
处理结果
当用户完成选择后,系统会返回 RESULT_OK 和会话 URI。此 URI 授予对所选数据的临时读取权限。
您可以使用标准 ContentResolver 查询此 URI。生成的 Cursor 包含所请求的数据字段,并遵循 ContactsContract.Data 的架构。
private val pickContactLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// The result data contains the Session URI
val sessionUri = result.data?.data
sessionUri?.let { uri ->
processSelectedContacts(uri)
}
} else {
// User cancelled the picker
}
}
private fun processSelectedContacts(sessionUri: Uri) {
// Define the projection (columns) you want to retrieve
val projection = arrayOf(
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
ContactsContract.Data.MIMETYPE,
ContactsContract.Data.DATA1 // Generic data column (Phone number, Email, etc.)
)
contentResolver.query(sessionUri, projection, null, null, null)?.use { cursor ->
val mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)
val dataIdx = cursor.getColumnIndex(ContactsContract.Data.DATA1)
val nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)
while (cursor.moveToNext()) {
val mimeType = cursor.getString(mimeTypeIdx)
val dataValue = cursor.getString(dataIdx)
val name = cursor.getString(nameIdx)
when (mimeType) {
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
Log.d("ContactPicker", "Picked Phone: $dataValue for $name")
}
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> {
Log.d("ContactPicker", "Picked Email: $dataValue for $name")
}
}
}
}
}
向后兼容性
对于以 Android 17 及更高版本为目标平台的应用,系统会自动升级现有的 Intent.ACTION_PICK intent 以使用新的联系人选择器界面。
如果您的应用已使用 ACTION_PICK,则无需更改代码即可获得新界面。不过,如需使用新功能(例如接收单个 Uri 来查询联系人数据、在个人资料和工作资料之间切换或发出多个数据字段请求),您必须更新实现以使用 Intent.ACTION_PICK_CONTACTS 或新的 intent extra。
在旧版 SDK 上进行测试
您可以在搭载 Android 17 及更高版本的设备上测试新的选择器行为,即使您的应用以较低的 SDK 版本为目标平台,也可以通过向 ACTION_PICK intent 添加 EXTRA_USE_SYSTEM_CONTACTS_PICKER 布尔值 extra 来实现此目的。
最佳做法
- 仅请求所需权限 :如果您的应用只需要发送短信,请请求
Phone.CONTENT_ITEM_TYPE。选择器会自动过滤掉没有电话号码的联系人,从而为用户提供更简洁的界面。 - 立即持久保留数据:会话 URI 授予临时读取权限。如果您需要在应用进程被终止后访问此联系信息,则应用必须保留联系人数据。
- 请勿依赖账号数据:为保护用户隐私并防止指纹识别,系统会从结果中剥离账号特定的元数据。
版本说明
|--------------------|-----------------------------------------------------------------------------------------|
| 发布日期 | 2026 年 2 月 13 日 |
| build | CP21.260116.011.B1 CP21.260116.011.A1(Pixel 6 Pro、Pixel 6、Pixel 6a、Pixel 7 Pro、Pixel 7) |
| 模拟器支持 | x86(64 位)、ARM (v8-A) |
| 安全补丁级别 | 2026-01-05 |
| Google Play 服务 | 25.47.33 |
|--------------------|--------------------------------------------------------------------------------------|
| 发布日期 | 2026 年 2 月 26 日 |
| build | CP21.260206.011 CP21.260206.011.A1(Pixel 6 Pro、Pixel 6、Pixel 6a、Pixel 7 Pro、Pixel 7) |
| 模拟器支持 | x86(64 位)、ARM (v8-A) |
| 安全补丁级别 | 2026-02-05 |
| Google Play 服务 | 25.49.33 |
Android 17 Beta 2(2026 年 2 月)
Beta 2 现已发布。 与 Beta 1 类似,此版本适用于开发、测试和一般用途。不过,Android 17 仍处于积极开发阶段,因此该 Android 系统和搭载该系统的应用可能无法始终按预期工作。
Beta 2 的新变化
用户体验和系统界面
- 气泡 :用户现在可以通过长按启动器图标来将任何应用设为气泡。在大屏设备上,任务栏中的新气泡栏 可管理已整理和锚定的气泡。应用应遵循多窗口指南。
- EyeDropper API:借助新的系统 API,应用可以从显示屏上的任意位置捕获像素颜色,而无需屏幕捕获权限。
- 联系人选择器 :ACTION_PICK_CONTACTS intent 提供了一个系统级选择器。它可授予对特定字段的临时会话级访问权限,从而减少对完整 READ_CONTACTS 权限的需求。
- 触控板指针捕获 :默认情况下,捕获的触控板现在像鼠标一样,报告相对移动和手势,而不是原始手指坐标。旧版绝对模式仍可通过
POINTER_CAPTURE_MODE_ABSOLUTE使用。 - 互动式选择器 :应用可以在 ChooserSession 上使用 getInitialRestingBounds 来确定选择器的最终界面位置,以便更好地调整布局。
连接性和跨设备
- 跨设备切换 :借助新的 Handoff API,您可以通过 CompanionDeviceManager 在不同设备(例如从手机到平板电脑)之间恢复状态。
- 高级测距 :
- UWB DL-TDOA:支持 FiRA 4.0,可实现保护隐私的室内导航。
- 附近设备检测:实现 WiFi 联盟规范,以改进基于 WiFi 的测距。
- 数据流量套餐增强功能 :应用可以使用 getStreamingAppMaxDownlinkKbps 和 getStreamingAppMaxUplinkKbps 查询运营商分配的流式传输下行链路/上行链路最大速率。
核心功能、隐私权和性能
- 本地网络访问权限 :Android 17 引入了 ACCESS_LOCAL_NETWORK 权限(属于 NEARBY_DEVICES 组)来保护 LAN 通信。
- 时区广播 :新 intent ACTION_TIMEZONE_OFFSET_CHANGED 专门在发生偏移量变化(例如 DST 过渡)时触发。
- NPU 管理 :以 Android 17 为目标平台的应用必须声明 FEATURE_NEURAL_PROCESSING_UNIT 硬件功能才能直接访问 NPU。
- ICU 78 :更新了国际化库,以支持 Unicode 17。
- 短信动态密码保护 :为防止劫持,Android 17 会延迟对大多数应用的动态密码消息的程序化访问权限,延迟时间为 3 小时。开发者应改用 SMS Retriever 或 SMS User Consent API。
Beta 2 中修复的问题
- Android 16 中的平台稳定性回归问题,导致活跃应用意外重启或刷新,从而防止在应用使用期间丢失用户进度和出现间歇性界面闪烁。(问题 #440017096)
- 对于使用德语设置的用户,"最近使用的应用"界面中存在界面布局回归问题。(问题 #476830557、问题 #486511401)
- 通过在编码器配置后启用开发者通过 getOutputFormat 确认时间分层支持,改进了视频流式传输可靠性,以解决缺少帧依赖性元数据的问题。(问题 #306222291)
- 在弱光模式下,时钟屏保在 24 小时制格式中省略了前导零的 bug。(问题 #444255729)
- 关闭文件夹后,立即执行后续互动操作(例如打开另一个文件夹或切换屏幕)时受阻的问题。(问题 #470541347、问题 #471533397、问题 #477848604)
- 导致设备使用中断的系统崩溃和自发重启问题。(问题 413562426)
- 严重的系统不稳定性,导致设备在应用转换或服务调用期间冻结和重新启动。(问题 #419070024、问题 #428572458、问题 #430393241、问题 #424912278、问题 #431440391、问题 #426346396)
- 在与 Android Auto 断开连接后,导致锁定屏幕无响应和显示挂起的系统界面死锁。(问题 #457527675)
- 系统位置信息权限披露对话框中的界面错字,其中"返回"按钮错误地显示为"Bac"。(问题 #460242870、问题 #477245738)
- 实时翻译和规则在"系统"菜单中被错误归类的问题。(问题 #476754995)
- 反复进入"显示和触控"设置导致严重的系统界面崩溃和后续设备不稳定。(问题 #474486679)
- 导致用户无法从主屏幕打开"壁纸与个性化"设置的持续性崩溃。(问题 #478520173)
- 无线调试二维码扫描器中的界面布局问题,即返回箭头与二维码图标重叠。(问题 #474769647)
- 声音设置中存在一个问题,即选择铃声后,铃声预览无法播放。(问题 #355086959、问题 #375840924、问题 #381007949、问题 #381077928、问题 #419301121、问题 #452646483、问题 #468837747)
- 一个 bug,该 bug 会导致系统更新后出现冗余通知,通过改进通知服务逻辑,在更新后初始化过程中正确清除过时的提醒。(问题 #454647834)
- Pixel 6 Pro 上存在的 GPU 着色器编译器优化 bug,该 bug 会导致特定的 GLSL 数学表达式被错误地评估为常量,从而导致应用中出现视觉渲染伪影。(问题 #473226715)
Android 17 Beta 1(2026 年 2 月)
Beta 1 现已推出,其中包含最新功能和变更,供您在应用中试用。此版本适用于开发、测试和一般用途。不过,Android 17 仍处于积极开发阶段,因此该 Android 系统和搭载该系统的应用可能无法始终按预期工作。
与之前的版本一样,Android 17 包含一些系统变更。在某些情况下,在应用更新为支持 Android 17 之前,这些变更可能会对应用产生一些影响,因此您可能会遇到从各种小问题到更严重的限制等各种影响。一般来说,大多数应用以及大多数 API 和功能都会按预期运行。
Beta 1 的新变化
Android 17 继续致力于打造更具适应性的 Android 应用,大幅增强相机和媒体功能,推出用于优化连接的新工具,并为配套设备扩展了配置文件。亮点聚焦:
界面和窗口
强制性大屏自适应
以 Android 17(API 级别 37) 为目标平台且在大屏(sw ≥ 600dp)上运行的应用无法再选择不调整大小或更改屏幕方向。
- 忽略的属性 :screenOrientation、resizeableActivity、minAspectRatio 和 maxAspectRatio 在大屏幕上会被忽略。
- 例外情况 :屏幕宽度小于 600dp 的设备以及归类为游戏 (
android:appCategory) 的应用。
优化配置变更
为防止状态丢失,系统默认情况下不再针对特定配置更改重启 activity,包括:
- CONFIG_KEYBOARD / CONFIG_KEYBOARD_HIDDEN
- CONFIG_NAVIGATION
- CONFIG_TOUCHSCREEN
- CONFIG_COLOR_MODE
- CONFIG_UI_MODE(仅当界面模式更改为 UI_MODE_TYPE_DESK 或从 UI_MODE_TYPE_DESK 更改为其他类型时)
需要采取行动:如果您的应用依赖于重新启动来重新加载这些事件的资源,则必须使用新的 android:recreateOnConfigChanges 清单属性明确选择启用此行为。
性能和运行时
- 无锁 MessageQueue :android.os.MessageQueue 的新无锁实现可减少丢帧。
- 分代垃圾回收:ART 的并发标记-紧凑型回收器现在支持分代垃圾回收,优先进行频繁且低成本的"年轻代"回收。
- 新的分析触发器 :ProfilingManager 为 COLD_START、OOM 和 KILL_EXCESSIVE_CPU_USAGE 添加了触发器。
- 通知限制:对自定义通知视图强制执行严格的大小限制,以减少内存用量。
媒体和相机
相机
- 动态会话更新 :使用 CameraCaptureSession.updateOutputConfigurations() 切换使用情形(例如,照片到视频)而不会关闭会话或导致故障。
音频和视频
- 视频录制的恒定质量 :MediaRecorder 中的 setVideoEncodingQuality() 可让您为视频编码器配置恒定质量 (CQ) 模式。
- 后台音频强化:如果应用未处于有效的生命周期状态,音频播放、焦点请求和音量更改会静默启动(失败)。
- VVC 支持 :添加了对多功能视频编码 (H.266) 的平台支持。
隐私权和安全性
- 明文弃用 :android:usesCleartextTraffic 已弃用。如果以 SDK 37 及更高版本为目标平台的应用依赖于此属性,则默认会阻止明文;请迁移到网络安全配置。
- HPKE 混合加密 :为 HPKE 混合加密的实现引入了公共服务提供程序接口。
连接和工具
- Companion Device Manager:
- 新配置文件 :医疗设备和健身追踪器。
- 统一权限对话框 :setExtraPermissions 将附近权限捆绑到关联对话框中。