1. 获取 Android 17

1. 获取 Android 17

1. 获取 Android 17谷歌发布时间表Android 17 预计在 2026 年第二季度(26Q2)发布正式版本。目前处于开发阶段,部分特性已通过预览版向开发者开放。

在 Google Pixel 设备上获取 Android 17开发者持有 Pixel 系列的机器可以直接 OTA 升级,或者下载镜像升级。具体可参考:

在 Google Pixel 设备上获取 Android 17 Beta 版适用于 Google Pixel 的出厂映像适用于 Google Pixel 的 OTA 映像设置 Android 模拟器请参考 设置 Android 模拟器。

设置 Android 17 SDK请参考 设置 Android 17 SDK。

小米手机获取Android 17为方便开发者第一时间对小米手机进行Android™ 17 Beta 2的升级适配,即日起 Xiaomi 17 Ultra、Xiaomi 17 Ultra 徕卡版、Xiaomi 17 三款机型的开发者,可通过以下链接卡刷基于Android™ 17 Beta 2的小米澎湃OS 开发者预览版。

卡刷过程会清除数据,请您务必提前备份好数据,避免个人数据丢失。尝鲜公告:https://web.vip.miui.com/page/info/mio/mio/detail?postId=52735582刷机指南:https://web.vip.miui.com/page/info/mio/mio/detail?isTop=1&postId=51780825

2.影响所有应用的行为变更安全usesCleartextTraffic 弃用预告Google 官方文档:行为变更:所有应用 - usesClearTraffic 弃用计划

一、特性背景android:usesCleartextTraffic 是 AndroidManifest 中用于控制应用是否允许明文(HTTP)网络流量的属性。Android 平台计划在未来版本中弃用该属性,届时即使设置 usesCleartextTraffic="true",系统也将完全忽略该值,明文流量将被平台网络栈默认拒绝。替代方案为网络安全配置文件(Network Security Config),它提供更精细的域名级别控制能力,支持仅对特定域名放行的明文流量,是当前官方推荐的配置方式(API 24 起支持)。

二、适用范围

维度说明正式生效版本预计 Android 18(以谷歌官方文档为准)受影响应用依赖 usesCleartextTraffic="true" 允许 HTTP 流量、且未配置 Network Security Config 的应用不受影响已使用 Network Security Config 文件的应用;targetSdk <= 37 的应用

三、特性内容Android 17 在 ManifestConfigSource.java 中新增了 CompatChange DEPRECATE_USES_CLEARTEXT_TRAFFIC(ID: 415007211,当前 @Disabled)和 aconfig flag deprecate_uses_cleartext_traffic2。当两者同时启用时,系统将强制忽略 usesCleartextTraffic 属性值。同时,R.attr.usesCleartextTraffic 已在公开 API 中标注为 @Deprecated @FlaggedApi。

四、应用适配建议现在迁移,避免未来 Android 18(以谷歌官方文档为准) 正式生效时被动修改。第一步:检查是否受影响在 AndroidManifest.xml 中搜索:

android:usesCleartextTraffic="true"若存在且未配置 android:networkSecurityConfig,则需要迁移。

第二步:按 minSdkVersion 选择迁移方案情况一:minSdkVersion < 24(需兼容 Android 7 以下)同时保留 usesCleartextTraffic="true" 和 Network Security Config,两者并存:

api.example.com

情况二:minSdkVersion >= 24(仅支持 Android 7 及以上)直接使用 Network Security Config,无需 usesCleartextTraffic:

限制隐式 URI 授权

Google 官方文档:行为变更:所有应用 - 限制隐式 URI 授权一、特性背景Android 系统此前对 ACTION_SEND、ACTION_SEND_MULTIPLE、ACTION_IMAGE_CAPTURE 等包含 URI 的 Intent 自动授予读写权限(隐式 URI 授权),存在安全隐患。Android 17 引入限制框架和 StrictMode 检测,Android 18(API 38)将正式废止隐式授权。⚠️ Android 17 当前行为与 Android 16 完全相同,隐式授权仍然发生。 三个限制 Aconfig 标志默认关闭。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求StrictMode 自动检测:targetSdk > 36;限制本身:所有应用(Aconfig 标志控制)当前状态限制标志默认关闭,隐式授权仍发生;Android 18 预计正式废止

三、特性内容Android 17 在 Intent.migrateExtraStreamToClipData() 中对三类 Action 添加了条件分支:

ACTION_SEND:自动读取授权受 Aconfig 标志控制,标志开启时不再自动添加 FLAG_GRANT_READ_URI_PERMISSIONACTION_SEND_MULTIPLE、ACTION_IMAGE_CAPTURE:同理,读写权限的自动授予受 Aconfig 标志控制当前所有限制标志默认关闭,隐式授权仍然发生。logcat 中会输出 ERROR 日志提示 "discontinued from Android 18 onwards"。StrictMode 新增 CompatChange DETECT_IMPLICIT_URI_PERMISSION_GRANT(ID: 460838111),对 targetSdk > 36 的应用自动启用 StrictMode 检测。

四、应用适配建议现在迁移,避免 Android 18 正式废止时出现 SecurityException。发送方应用修复(显式添加授权标志)

// ACTION_SEND 修复 Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("image/jpeg"); shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 显式添加 startActivity(Intent.createChooser(shareIntent, "分享")); // ACTION_IMAGE_CAPTURE 修复 Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivityForResult(cameraIntent, REQUEST_IMAGE_CAPTURE);

开发阶段检测(targetSdkVersion >= 37,且 strictModeViolationForImplicitUriGrantsEnabled 标志开启时):

if (BuildConfig.DEBUG) { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectImplicitUriPermissionGrant() .penaltyLog() .build()); }

Keystore 密钥数量限制Google 官方文档:行为变更:所有应用 - 按应用密钥库限制一、特性背景Android 17 在 keystore2 守护进程中引入按 UID 的密钥数量上限:targetSdk >= 37 的非系统应用上限 50,000 个,其他应用 200,000 个。超出时抛出 KeyStoreException(targetSdk >= 37 错误码为 ERROR_TOO_MANY_KEYS)。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有非系统应用;targetSdk >= 37 上限更低(50,000 vs 200,000),且获得专属错误码 ERROR_TOO_MANY_KEYS当前状态已生效三、特性内容密钥数量上限(由 keystore2 守护进程实现):

应用类型targetSdk上限非系统应用>= 3750,000 个非系统应用< 37200,000 个系统应用任意200,000 个四、应用适配崩溃/行为特征

java.security.ProviderException: Keystore key generation failed Caused by: android.security.KeyStoreException: Too many keys (errorCode: 29 或 30)捕获并处理超限异常

try { KeyGenerator keyGen = KeyGenerator.getInstance("AES", "AndroidKeyStore"); keyGen.init(spec); SecretKey key = keyGen.generateKey(); } catch (ProviderException e) { Throwable cause = e.getCause(); if (cause instanceof KeyStoreException) { KeyStoreException kse = (KeyStoreException) cause; if (kse.getNumericErrorCode() == KeyStoreException.ERROR_TOO_MANY_KEYS || kse.getNumericErrorCode() == KeyStoreException.ERROR_INCORRECT_USAGE) { // 清理旧密钥后重试 cleanupUnusedKeys(); } } }适配建议

为密钥设置有效期(setKeyValidityEnd),并定期清理不再使用的密钥(KeyStore.deleteEntry(alias))避免为每次操作创建独立密钥,改为复用已有密钥升级 targetSdk 至 37 前,确认应用密钥总数不超过 50,000(通过枚举 KeyStore.aliases() 检查)用户体验和系统界面旋转后恢复 IME 可见性Google 官方文档:行为变更:所有应用 - 在旋转后恢复默认 IME 可见性一、特性背景Android 17 改变了旋转后 IME 恢复逻辑:不再依据 mLastImeShown 标志自动恢复 IME,改为读取新窗口的 requestedVisibleTypes(默认不含 IME)。旋转后 IME 不再自动出现,需应用显式请求。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(由 Aconfig Flag 控制)当前状态由 Aconfig Flag disable_ime_restore_on_activity_create 控制

三、特性内容Android 17 通过 Aconfig Flag disable_ime_restore_on_activity_create 改变了 IME 恢复判断逻辑:

Flag 关闭(旧行为):系统检查 ActivityRecord.mLastImeShown,若旋转前 IME 可见则自动恢复Flag 开启(新行为):系统改为检查新窗口的 requestedVisibleTypes,Activity 重建后新窗口该字段默认不含 IME,因此 IME 不再自动恢复,需应用显式请求四、应用适配受影响场景:应用未处理旋转(Activity 重建),且旋转前 IME 通过用户操作打开(非 stateAlwaysVisible)。Flag 开启后,旋转后 IME 不再自动出现。不受影响:windowSoftInputMode=stateAlwaysVisible;应用自行处理 configChanges(Activity 不重建);应用已在 onCreate() 中显式请求 IME。适配方案方案一:在 Manifest 中设置 stateAlwaysVisible

方案二:在 onCreate() 中显式请求

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); editText.post(() -> { editText.requestFocus(); InputMethodManager imm = getSystemService(InputMethodManager.class); imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); }); }方案三:自行处理配置变化(Activity 不重建,IME 状态保持)

人工输入触控板指针捕获默认传递相对事件Google 官方文档:行为变更:所有应用 - 在指针捕获期间,触控板默认传递相对事件一、特性背景Android 17 将触控板在指针捕获期间的默认行为从 ABSOLUTE(原始绝对坐标)改为 RELATIVE(相对移动,与鼠标一致)。通过两个 Aconfig Flag 控制,不受 targetSdkVersion 影响。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(由 Aconfig Flag 控制)当前状态双 Flag 控制:pointer_capture_modes + relative_capture_mode_by_default(两个均需开启)三、特性内容Android 17 新增三个指针捕获模式常量(View.java):

常量值说明POINTER_CAPTURE_MODE_UNCAPTURED0未捕获POINTER_CAPTURE_MODE_ABSOLUTE1旧行为:绝对坐标POINTER_CAPTURE_MODE_RELATIVE2新默认:相对坐标(鼠标风格)新增 requestPointerCapture(int mode) 重载,允许应用显式指定模式。无参 requestPointerCapture() 在两个 Flag 均开启时默认使用 RELATIVE 模式。

四、应用适配兼容性问题

问题原因多触点绝对坐标处理失效RELATIVE 模式不暴露多触点数据,双指捏合变为 ACTION_SCROLL事件源判断逻辑失效getSource() == SOURCE_TOUCHPAD 不再匹配,RELATIVE 模式返回 SOURCE_MOUSE_RELATIVEgetX()/getY() 语义变化RELATIVE 模式下返回相对位移量,而非绝对坐标迁移方案若应用依赖触控板多触点绝对坐标(如自定义手势识别),需显式指定 ABSOLUTE 模式:

// 在 Flag 开启的 Android 17+ 设备上,显式请求 ABSOLUTE 模式(保持旧行为) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN && com.android.hardware.input.Flags.pointerCaptureModes()) { view.requestPointerCapture(View.POINTER_CAPTURE_MODE_ABSOLUTE); } else { view.requestPointerCapture(); }若应用只需要"鼠标风格的移动+滚轮"(如 FPS 游戏),无需修改代码,新默认行为已自动处理触控板。

媒体后台音频操作强化(Background Audio Hardening)Google 官方文档:行为变更:所有应用 - 后台音频强化 | 详细指南一、特性背景Android 平台要求音频操作只能由前台应用或持有 WIU(While-In-Use)能力的前台服务执行。Android 16 仅记录日志,Android 17 通过 aconfig flag 正式开启强制阻断模式:后台调用音量 API 静默失败,后台请求焦点返回 AUDIOFOCUS_REQUEST_FAILED,后台播放被系统静默。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(由 Aconfig Flag 控制)当前状态由多个 aconfig flag 控制:hardening_strict(核心 flag)、hardening_partial_volume(音量方法)、ro_foreground_audio_control(FGS WIU 能力)三、特性内容Android 17 将音频强化从 Android 16 的 warning 模式升级为 strict 强制模式,在 aconfig flag 开启后:

音量方法(adjustStreamVolume、setStreamVolume 等):后台调用时静默 no-op,直接 return,不抛异常音频焦点(requestAudioFocus):后台调用时返回 AUDIOFOCUS_REQUEST_FAILED(0)音频播放(AudioTrack.write() / AAudio / OpenSL ES):后台播放被系统静默(MUTED_BY_OP_CONTROL_AUDIO)核心通过三个 AppOps 实现:OP_CONTROL_AUDIO、OP_CONTROL_AUDIO_PARTIAL、OP_TAKE_AUDIO_FOCUS,结合 PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL 进程能力判断应用是否有权执行音频操作。

四、应用适配受影响 API后台调用以下 API 时静默失败(no-op,无异常):adjustStreamVolume、setStreamVolume、setStreamMute、adjustVolume、adjustSuggestedStreamVolume、setRingerMode。requestAudioFocus 返回 AUDIOFOCUS_REQUEST_FAILED(0)。AudioTrack.write() / AAudio / OpenSL ES 后台播放被系统静默。

豁免情况:应用有可见 Activity(前台)、应用可见时启动的非 SHORT_SERVICE 类型 FGS、system-server 委托启动的 FGS(如 Telecom)、持有 MODIFY_AUDIO_SETTINGS_PRIVILEGED / MODIFY_AUDIO_ROUTING / MODIFY_PHONE_STATE 权限。⚠️ 不豁免:从后台启动的 FGS(BFSL,如 BOOT_COMPLETE 触发)。

WIU 能力关键:FGS 获得 WIU 的关键在于启动时应用是否可见。从后台启动的 FGS(如 BOOT_COMPLETE)无法获得 WIU。VOIP 应用通常已通过 Telecom Jetpack API 满足要求,不太可能受影响。修复方案方案一(推荐):在应用可见时启动 mediaPlayback 前台服务,再执行音频操作。短暂故障时保持 FGS 有效(< 10 分钟),收到永久性结束信号时才停止 FGS。方案二:确保在前台 Activity 中调用音频 API,避免在 onStop() 后触发。方案三:处理静默失败(检查 requestAudioFocus 返回值是否为 AUDIOFOCUS_REQUEST_FAILED)。

logcat 识别关键词:AudioHardening volume control ... ignored、AudioHardening focus request ... ignored、AudioHardening background playback muted本地验证命令:

adb shell cmd audio set-enable-hardening 1 # 启用adb shell cmd audio set-enable-hardening 0 # 停用

连接蓝牙绑定丢失的自主重新配对Google 官方文档:行为变更:所有应用 - 针对蓝牙绑定丢失的自主重新配对一、特性背景Android 16 及之前,蓝牙设备丢失配对信息时每次都广播 ACTION_KEY_MISSING。Android 17 引入自主重新配对机制:系统可在后台静默恢复 bond key,成功时不再广播。同时 ACTION_KEY_MISSING 升级为受保护广播。

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(由 Aconfig Flag 控制)当前状态由 autonomous_repairing_initiation + bluetooth_pairing_hardening 两个 flag 控制;ACTION_KEY_MISSING 受保护广播已无条件生效三、特性内容Android 17 的蓝牙自主重新配对特性包含以下核心变化:

自主重配对机制:系统可在后台静默完成 bond key 恢复,成功时不再广播 ACTION_KEY_MISSING,仅在自主重配对失败时才广播受保护广播:ACTION_KEY_MISSING 升级为 ,第三方应用无法再伪造该广播Settings UI:key missing 时连接摘要优先显示 "Can't connect"四、应用适配ACTION_KEY_MISSING 广播接收行为变化

场景Android 16Android 17(flag 开启后)系统自主重配对成功广播发送不发送广播系统自主重配对失败广播发送广播发送第三方应用发送此广播允许被系统拦截(受保护广播)适配建议

不可单独依赖 ACTION_KEY_MISSING;同时监听 ACTION_BOND_STATE_CHANGED(BOND_NONE)感知 bond 丢失监听 ACTION_PAIRING_REQUEST 时,检查 EXTRA_PAIRING_CONTEXT 区分自主重配对与手动配对// 推荐:同时监听 bond 状态IntentFilter filter = new IntentFilter();filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);filter.addAction(BluetoothDevice.ACTION_KEY_MISSING);// 仍可监听,但不再保证每次 key missing 都收到 registerReceiver(receiver, filter); // 在 BroadcastReceiver 中 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1); if (bondState == BluetoothDevice.BOND_NONE) { // bond 完全解除,可提示用户重新配对 }

核心功能已回收 Parcel 对象的访问限制一、特性背景Parcel 通过对象池(obtain() / recycle())复用。Android 16 中 recycle() 后继续读取仅打印警告。Android 17 新增 assertNotRecycled() 检测,检测到 use-after-recycle 时直接抛出 BadParcelableException。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(无 CompatChange,无 targetSdk 门槛)当前状态已生效,当前仅在 readParcelableInternal() 入口处调用三、特性内容Android 17 在 Parcel.java 中新增 assertNotRecycled() 检测,目前被插入 readParcelableInternal() 入口处。当检测到 Parcel 回收后继续被读取时,直接抛出 BadParcelableException(Android 16 及之前仅打印 Log.wtf 警告,不抛异常)。注意:当前仅 readParcelable() 路径触发此检查;后续 Android 版本将逐步扩展到更多读写方法。

四、应用适配如何判断是否受影响:搜索 recycle() 后仍对同一 Parcel 调用读取方法的用法,或多线程未同步复用 Parcel 的场景。崩溃/异常特征

android.os.BadParcelableException: Parcel used while recycled. ... at android.os.Parcel.errorUsedWhileRecycling(Parcel.java:656) at android.os.Parcel.assertNotRecycled(Parcel.java:662) at android.os.Parcel.readParcelableInternal(Parcel.java:5261) at android.os.Parcel.readParcelable(Parcel.java:...) at com.example.app.MyClass.someMethod(MyClass.java:...)修复/迁移方案典型错误模式及修复:

// 错误:recycle() 后继续读取 Parcel parcel = Parcel.obtain(); parcel.setDataPosition(0); SomeData data = parcel.readParcelable(SomeData.class.getClassLoader()); parcel.recycle(); // 错误:以下调用将触发 BadParcelableException(Android 17) SomeData data2 = parcel.readParcelable(SomeData.class.getClassLoader()); // 修复:recycle() 前完成所有读取操作,recycle() 后不再访问该对象 Parcel parcel = Parcel.obtain(); parcel.setDataPosition(0); SomeData data = parcel.readParcelable(SomeData.class.getClassLoader()); SomeData data2 = parcel.readParcelable(SomeData.class.getClassLoader()); // 在 recycle 前完成 parcel.recycle(); // parcel 不再使用多线程场景下需自行记录 recycle 状态(isRecycled() 非公开 API),确保 recycle() 后不再访问该对象。

Parcel 序列化大小校验(failOnParcelSizeMismatch)一、特性背景Parcel.writeValue() 序列化 Parcelable 等对象时写入 length prefix,readValue() 反序列化后校验字节数是否一致。若 writeToParcel() 与 createFromParcel() 字段不对称,会产生大小不一致。Android 16 仅打印警告,Android 17 通过 aconfig flag fail_on_parcel_size_mismatch 控制:flag 开启后直接抛出 BadParcelableException。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(由 aconfig flag 全局控制)当前状态已生效(flag fail_on_parcel_size_mismatch 在所有发布配置中均为 ENABLED)

三、特性内容Parcel.writeValue() 在序列化"带长度前缀"类型(Parcelable、Map、List、SparseArray、Serializable 等)时,会在对象内容之前写入字节数作为 length prefix。readValue() 在反序列化后比较实际消耗字节数与 length prefix 是否一致。Android 17 通过 aconfig flag fail_on_parcel_size_mismatch 控制不一致时的行为:

Flag 开启(Android 17 默认):抛出 BadParcelableExceptionFlag 关闭:仅记录 Slog.wtfStack 日志,继续执行(Android 16 行为)四、应用适配如何判断是否受影响搜索所有 Parcelable 自定义类,检查 writeToParcel() 与 createFromParcel() 是否严格对称(字段数量和顺序完全一致)。常见错误:遗漏读取、条件分支不对称、版本迭代添加新字段忘记对应读取。崩溃特征

android.os.BadParcelableException: Unparcelling of of type PARCELABLE consumed bytes, but expected. [throwing] at android.os.Parcel.readValue(Parcel.java:4761) at android.os.Parcel.readValue(Parcel.java:4736) at android.os.BaseBundle.getValue(BaseBundle.java:...) at android.os.Bundle.getParcelable(Bundle.java:...) at com.example.app.YourClass$CREATOR.createFromParcel(YourClass.java:...)

触发路径:通过 Bundle.getParcelable()、Parcel.readValue() 等方法反序列化自定义 Parcelable 时,若检测到字节数不一致则抛出该异常。

修复方案确保 writeToParcel() 与 createFromParcel() 严格对称:

// 错误示例:writeToParcel 写入了额外字段,createFromParcel 未对应读取 public class MyData implements Parcelable { public int value; public String name; public boolean flag; // 新增字段 @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(value); out.writeString(name); out.writeBoolean(flag); // 写入了 flag } protected MyData(Parcel in) { value = in.readInt(); name = in.readString(); // 错误:遗漏了 in.readBoolean(),导致字节数不一致 } } // 修复:createFromParcel 必须与 writeToParcel 完全对称 protected MyData(Parcel in) { value = in.readInt(); name = in.readString(); flag = in.readBoolean(); // 补充读取,与写入对称 }如果需要兼容旧版本序列化数据,建议通过版本号字段管理序列化格式(在 writeToParcel 头部写入版本号,createFromParcel 按版本号读取对应字段)。

相关配置变更不再重启 Activity一、特性背景Android 17 引入 SKIP_ACTIVITY_RECREATION_ON_CONFIG_CHANGE:当键盘、导航、触控屏、色彩模式等"低优先级"配置变更发生时,系统默认不再重启 Activity,转而调用 onConfigurationChanged() 回调。对所有应用生效。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(@Overridable)当前状态flag 开启后对所有应用生效三、特性内容受影响的配置变更类型:

Manifest 声明值说明keyboard键盘类型变化keyboardHidden软键盘弹出/收起navigation导航方式变化touchscreen触控屏配置变化colorMode色彩模式变化(HDR 等)UI_MODE仅限进入/离开 UI_MODE_TYPE_DESK 时新增 manifest 属性 android:recreateOnConfigChanges,允许应用逐 Activity 声明哪些配置变更仍需重建:

四、应用适配受影响场景若应用依赖 Activity 重建来重新加载这些配置对应的资源(例如在 onCreate 中根据键盘状态初始化布局,但未实现 onConfigurationChanged),则在 flag 开启后,配置变更不再触发重建,布局可能无法正确响应。方法一(推荐):实现 onConfigurationChanged() 主动处理

@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 主动响应 keyboard / keyboardHidden / navigation / touchscreen / colorMode 变化 updateLayoutForConfig(newConfig); }方法二:通过 manifest 属性恢复特定配置变更时的重建行为

android:recreateOnConfigChanges 是 Android 17 新增属性,在旧版本设备上系统会忽略该未知属性,无副作用。

文件操作模式严格校验一、特性背景ParcelFileDescriptor.parseMode(String) 用于将文件打开模式转换为 POSIX 标志位。旧实现采用逐字符扫描,对未文档化的模式(如 rwa、ra、rt)会静默接受。Android 17 改为显式枚举 switch 语句,不在白名单中的模式直接抛出 IllegalArgumentException。对所有应用生效,无 CompatChange 保护。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(无 CompatChange 保护)当前状态已无条件生效三、特性内容Android 17 将 FileUtils.translateModeStringToPosix() 从逐字符扫描改为显式枚举的 switch 语句,任何不在白名单中的模式均直接抛出 IllegalArgumentException。合法模式白名单(Android 17)

模式等价 POSIX 标志说明rO_RDONLY只读wO_WRONLY | O_CREAT只写,不存在则创建wt 或 twO_WRONLY | O_CREAT | O_TRUNC只写,截断wa 或 awO_WRONLY | O_CREAT | O_APPEND只写,追加rw 或 wrO_RDWR | O_CREAT读写rwt/rtw/wrt/wtr/trw/twrO_RDWR | O_CREAT | O_TRUNC读写,截断

四、应用适配判断是否受影响搜索代码中 parseMode()、openFileDescriptor(uri, mode) 等调用,重点检查 "rwa"、"ra"、"rt" 等矛盾模式(任何包含 a 但基础模式为 r 的组合均为非法)。崩溃特征

java.lang.IllegalArgumentException: Bad mode: rwa at android.os.FileUtils.translateModeStringToPosix(FileUtils.java:1552) at android.os.ParcelFileDescriptor.parseMode(ParcelFileDescriptor.java:679)修复方案将非法/矛盾模式替换为文档明确支持的合法模式:

// 错误:rwa 在 Android 17 中抛异常 ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode("rwa")); // 修复:根据实际需求选择合法模式 // 若需要读写 + 追加,使用 "rw" 打开后通过 seek 到末尾实现 ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode("rw")); // 或者,若仅需追加写入,使用 "wa" ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode("wa"));ContentResolver.openFileDescriptor(uri, mode) 同理,将 "rwa" 替换为 "rw" 或 "wa"。

线程优先级越界强制校验一、特性背景Process.setThreadPriority() 接受 Linux nice 值(-20 到 19)。Android 16 及之前越界参数由内核静默 clamp。Android 17 改为 Java 层校验,传入越界值直接抛出 IllegalArgumentException。对所有应用生效。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(无 CompatChange 保护)当前状态已生效

三、特性内容Android 17 将 Process.setThreadPriority() 的两个重载从 native 方法改写为 Java 方法,在调用底层实现之前先进行参数校验:传入超出 [-20, 19] 范围的值将直接抛出 IllegalArgumentException(Android 16 及之前由内核静默 clamp 到合法范围)。合法范围:-20(最高优先级)到 19(THREAD_PRIORITY_LOWEST,最低优先级)。

四、应用适配如何判断是否受影响搜索 setThreadPriority 调用,检查参数是否可能超出 [-20, 19]。重点排查:硬编码越界值、外部来源未校验的值、对 THREAD_PRIORITY_LOWEST(19)做算术运算(如 +1 = 20)。修复/迁移方案在调用前将 priority 值 clamp 到合法范围,或直接使用 Process 类预定义的常量:

// 方案一:调用前主动 clamp,复现 Android 16 的静默截断行为 int safePriority = Math.max(-20, Math.min(Process.THREAD_PRIORITY_LOWEST, priority)); Process.setThreadPriority(safePriority); // 方案二:使用预定义常量,避免硬编码越界数值 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // = 10 Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); // = 0 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); // = -2 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); // = -19崩溃/异常特征

java.lang.IllegalArgumentException: Priority/niceness 20 is invalid at android.os.Process.setThreadPriority(Process.java:1388) at com.example.app.MyWorkerThread.run(MyWorkerThread.java:xx)

3.影响 targetSdkVersion >= 37 应用的变更核心功能MessageQueue 新无锁实现Google 官方文档:行为变更:targetSdk 37+ - MessageQueue 的新无锁实现 | 详细指南一、特性背景Android 17 引入全新的 DeliQueue 无锁消息队列实现,替代旧的 synchronized 有序链表,通过 CAS 原子操作消除锁竞争。由 CompatChange USE_NEW_MESSAGEQUEUE(ID: 421623328)控制,targetSdk >= 37 自动激活。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37当前状态USE_NEW_MESSAGEQUEUE(ID: 421623328)

三、特性内容最关键的破坏性变更:当 DeliQueue 路径激活时(targetSdk >= 37),mMessages 字段恒为 null,消息实际存储在新的 MessageStack(Treiber Stack)中。mMessages 新增 @UnsupportedAppUsage(maxTargetSdk = BAKLAVA) 限制。

四、应用适配最高风险:通过反射访问 mMessages 字段

// 常见于 APM SDK、性能监控框架 Field f = MessageQueue.class.getDeclaredField("mMessages"); f.setAccessible(true); Message msg = (Message) f.get(looper.getQueue()); // ★ Android 17(targetSdk >= 37):msg 永远为 null!消息在 mStack 中其他受影响的私有 API

私有 APIAndroid 17 变化mMessages 字段DeliQueue 路径下恒为 nullnext() 方法新增 maxTargetSdk = BAKLAVA 废弃声明synchronized(this)不再保护 DeliQueue 内部状态消息对象复用禁用对象池(防止 CAS ABA 问题),Message.obtain() 直接 new Message()推荐替代方案

废弃用途官方替代测试中操作消息队列(反射 mMessages)TestLooperManager(Android 9+)监控消息派发(hook next())Looper.setMessageLogging() 或 Handler.Callback检查消息是否存在(遍历 mMessages)Handler.hasMessages()测试框架升级要求

Espresso 需升级到 3.7.0+(该版本使用 Android 16 引入的新 TestLooperManager API 与 Looper 安全交互)Robolectric 需升级到 4.17+ 并迁移到 @LooperMode(PAUSED)本地验证命令:

adb shell am compat enable USE_NEW_MESSAGEQUEUE adb shell am compat disable USE_NEW_MESSAGEQUEUE

static final 字段不可修改Google 官方文档:行为变更:targetSdk 37+ - 静态 final 字段现在不可修改一、特性背景Android 17 ART 强制禁止运行时修改 static final 字段(targetSdk >= 37)。反射修改抛 IllegalAccessException;JNI 修改导致进程崩溃。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37当前状态已生效三、特性内容ART 运行时在 ArtField::IsUnmodifiable() 中根据 targetSdkVersion 决定是否允许修改 static final 字段。以 Google 官方文档为准:targetSdk >= 37 的应用已受限制。两条路径的行为差异:

反射路径(Field.set()):抛出 IllegalAccessException,可捕获JNI 路径(SetStaticXxxField()):直接 LOG(FATAL),进程崩溃,无 Java 异常可捕获四、应用适配崩溃/异常特征反射路径:

java.lang.IllegalAccessException: Cannot set static final int field com.example.Config.MAX_RETRY at java.lang.reflect.Field.set(Field.java:...) at com.example.app.SomeClass.init(SomeClass.java:xx)JNI 路径(无 Java 异常,进程直接终止):

A/art: art/runtime/jni/jni_internal.cc:1651] Cannot set static final int field com.example.Config.MAX_RETRYA/art: runtime aborting...

如何判断是否受影响:搜索通过反射 setAccessible(true) 或 JNI SetStaticXxxField 修改 static final 字段的用法(常见于配置覆盖、Mockito/PowerMock 测试 Mock)。修复方案

移除对 static final 字段的运行时修改:改为使用非 final 字段、配置文件或依赖注入测试框架:升级 Mockito(4.x+ 默认不修改 final 字段)、移除 PowerMock 依赖若必须兼容:将 targetSdk 保持在 36 以下可暂时回避,但不建议长期依赖通知自定义视图大小限制一、特性背景Android 17 对 targetSdk >= 37 的应用加强自定义通知视图内存限制:新增 SystemUI 侧 lightenPayload() 机制,超限时将自定义视图降级为标准通知模板(而非直接丢弃)。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37当前状态已定义,flag + CompatChange 双重门控三、特性内容分为两个层级的限制:NMS 层(发送时):与 Android 16 相同——inflate 后 View 内存 > 2MB 警告,>= 5MB 移除 custom view。SystemUI 层(显示时,Android 17 新增):CompatChange CHECK_SIZE_OF_INFLATED_CUSTOM_VIEWS(ID: 270553691),当 flag 开启且 targetSdk >= 37 时,若自定义 RemoteViews 超过阈值,lightenPayload() 将自定义视图降级为标准通知模板(标题/文本保留,自定义布局丢失)。受影响的 Notification.Builder 方法:setCustomContentView()、setCustomBigContentView()、setCustomHeadsUpContentView()。

四、应用适配受影响场景

自定义通知布局中包含从网络加载的高分辨率 Bitmap(封面图、用户头像等)自定义通知 layout 嵌套了复杂视图层级,inflate 后超过 2MB检测方式

# 观察 NMS 层警告日志 adb logcat | grep -i "custom.*view\|inflat.*notif"

适配建议

图片降采样:设置 Bitmap 前先降采样,通知图片建议不超过 1024×1024px使用 DecoratedCustomViewStyle:用 Notification.Builder.setStyle(new DecoratedCustomViewStyle()) 包装,系统会进行额外内存管理简化视图层级:减少 RemoteViews layout 嵌套深度设置 fallback 文本:确保 setContentTitle() 和 setContentText() 有有意义的文本,即使自定义视图被降级,通知内容依然可读NPU 直接访问需声明硬件功能一、特性背景Android 17 引入 NpuManager 统一调度 NPU 资源。targetSdk >= 37 的应用若未声明 android.hardware.npu 硬件特性,NPU 直接访问将被拒绝。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37,且设备搭载 NPU 硬件当前状态npumanager_block_missing_feature flag 已 ENABLED三、特性内容NpuManager 在调度应用时,检查 PackageInfo.reqFeatures 列表中是否声明了 android.hardware.npu。对 targetSdk >= 37 且未声明该 feature 的应用,flag 开启后将 hasDirectAccess 设为 false,NPU 硬件调度器拒绝其直接推理请求。应用更新 Manifest 声明后,系统会动态解除阻断。

四、应用适配受影响场景:直接使用 NNAPI、TFLite、ML Kit 底层 NPU 路径的应用,targetSdk 升级到 37 后未声明 feature。无 NPU 硬件的设备不受影响。失败表现:推理不抛异常,静默回退 CPU 执行(性能下降)。logcat:NPU access is blocked.修复方法(一行 Manifest 声明)

使用 required="false" 而非 required="true",以保证应用能安装在无 NPU 的设备上,同时在有 NPU 的 Android 17+ 设备上获得直接访问权限。

无障碍

复杂 IME 实体键盘输入的无障碍支持Google 官方文档:行为变更:targetSdk 37+ - 复杂 IME 实体键盘输入的无障碍支持一、特性背景CJKV 语言 IME 输入经历多个阶段,屏幕阅读器无法区分,导致中间状态频繁播报。Android 17 新增 AccessibilityEvent 文本变更类型 API 和 TextAttribute.isTextSuggestionSelected(),让屏幕阅读器在撰写阶段静默、提交时才播报。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求所有应用(可选增强,不破坏现有行为)当前状态由 Aconfig Flag 控制

三、特性内容AccessibilityEvent 新增文本变更类型常量:

常量说明TEXT_CHANGE_TYPE_UNDEFINED默认/未定义TEXT_CHANGE_TYPE_IN_COMPOSITIONIME 撰写进行中(中间状态,未提交)TEXT_CHANGE_TYPE_COMMITTED_BY_IMEIME 提交操作触发(文本已定稿)TEXT_CHANGE_TYPE_CONVERSION_SUGGESTION_SELECTED_BY_IMEIME 候选词选中操作触发TextAttribute 新增 isTextSuggestionSelected() 方法和 Builder.setTextSuggestionSelected() setter,用于传递候选词选择状态。

四、应用适配本次变更属于可选增强,不破坏现有行为,Flag 未启用时行为与 Android 16 完全一致。CJKV IME 应用适配建议:在调用 InputConnection.setComposingText() 时,通过 TextAttribute 传递候选词选择状态:

if (Flags.a11yTextChangeTypesApi()) { TextAttribute textAttr = new TextAttribute.Builder() .setTextConversionSuggestions(suggestions) .setTextSuggestionSelected(true) // 候选词选择中 .build(); inputConnection.setComposingText(composingText, 1, textAttr); }

无障碍服务适配建议:根据事件类型调整播报策略:

if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED && Flags.a11yTextChangeTypesApi()) { int types = event.getTextChangeTypes(); if ((types & AccessibilityEvent.TEXT_CHANGE_TYPE_IN_COMPOSITION) != 0) { // 撰写中,可跳过或延迟朗读 } else if ((types & AccessibilityEvent.TEXT_CHANGE_TYPE_COMMITTED_BY_IME) != 0) { // IME 提交,触发最终朗读 } }隐私权机会性启用 ECH(加密客户端 Hello)Google 官方文档:行为变更:targetSdk 37+ - 机会性地启用 ECH一、特性背景ECH(Encrypted Client Hello)通过加密 TLS 握手中的 SNI 增强隐私保护。Android 17 对 targetSdk >= 37 的应用默认以 Opportunistic 模式启用 ECH(服务器支持时自动使用,不支持时正常握手),新增 XML 元素允许细粒度控制。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37 时默认启用 OPPORTUNISTIC 模式当前状态双重门控:CompatChange + Aconfig Flag 同时满足才启用

三、特性内容ECH 通过双重门控启用:CompatChange ENABLE_DEFAULT_ENCRYPTED_CLIENT_HELLO(targetSdk >= 37)+ Aconfig Flag 同时满足时,默认以 OPPORTUNISTIC 模式启用。新增 Network Security Config XML 元素 ,可在 中使用:

mode说明disabled禁用 ECHopportunistic服务器支持时启用 ECH,否则正常握手(targetSdk >= 37 默认值)enabled服务器支持时 ECH,不支持时使用 GREASE四、应用适配默认行为(无需修改代码):targetSdk >= 37 的应用在 Aconfig 标志开启的设备上,ECH 自动以 OPPORTUNISTIC 模式启用,应用无需任何改动即可受益。如需禁用 ECH(如企业网络代理不兼容 ECH 加密 SNI):

高风险场景:企业防火墙、DPI 设备依赖 TLS SNI 进行流量管控,ECH 加密 SNI 后可能导致连接被拒或降级。受影响时在 Network Security Config 中为相关域名设置 mode="disabled"。

访问本地网络需要权限

Google 官方文档:行为变更:targetSdk 37+ - 以 Android 17 为目标平台的应用需要本地网络权限一、特性背景Android 17 引入 ACCESS_LOCAL_NETWORK 运行时危险权限(NEARBY_DEVICES 权限组),通过内核 BPF 强制执行。targetSdk < 37 通过 split-permission 自动兼容;targetSdk >= 37 必须显式声明并请求。

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37 需显式声明和请求;targetSdk < 37 自动兼容当前状态已生效三、特性内容

ACCESS_LOCAL_NETWORK 权限定义为 dangerous 级别,属于 NEARBY_DEVICES 权限组向后兼容:targetSdk < 37 的应用通过 split-permission 机制(INTERNET → ACCESS_LOCAL_NETWORK)自动获得该权限BPF 强制执行:权限通过内核 BPF 层面强制,未授权时网络包被静默丢弃

四、应用适配需要权限的典型场景(targetSdk >= 37):LAN 直连 Socket、mDNS/DNS-SD 服务发现、Wi-Fi Direct、本地 HTTP 请求、UDP 广播/多播、智能家居设备控制。适配步骤第一步:在 AndroidManifest.xml 中声明:

第二步:运行时请求(与其他危险权限一致):

if (checkSelfPermission(Manifest.permission.ACCESS_LOCAL_NETWORK) != PackageManager.PERMISSION_GRANTED) { requestPermissions( new String[]{Manifest.permission.ACCESS_LOCAL_NETWORK}, REQUEST_CODE_LOCAL_NETWORK); }失败特征:Socket 连接超时或 Operation not permitted;BPF 层静默丢包(无 Java 异常);mDNS 发现无设备。

实体设备隐藏密码

Google 官方文档:行为变更:targetSdk 37+ - 在实体设备上隐藏密码一、特性背景Android 17 将密码可见性拆分为触屏和实体键盘两个独立设置。对 targetSdk >= 37 的应用,实体键盘输入密码时默认不再显示最后输入字符(TEXT_SHOW_PASSWORD_PHYSICAL 默认 0)。触屏行为不变。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37三、特性内容Android 17 新增 ShowSecretsSetting 类,将密码可见性拆分为两个独立设置:

设置默认值说明Settings.Secure.TEXT_SHOW_PASSWORD_TOUCH1(显示)触屏/软键盘输入,与旧版行为一致Settings.Secure.TEXT_SHOW_PASSWORD_PHYSICAL0(隐藏)实体键盘输入,这是行为变化

四、应用适配行为变化:targetSdk >= 37 的应用,用户通过外接键盘(蓝牙键盘、USB 键盘等)在密码字段输入时,所有字符立即显示为 •,最后输入字符不再短暂显示。触屏/软键盘输入不受影响。大多数应用无需修改:该变更由系统框架层(PasswordTransformationMethod)自动处理。若应用需读取密码可见性设置(需 Flag 开启):

boolean showTouch = ShowSecretsSetting.shouldShowTouchInput(context);boolean showPhysical = ShowSecretsSetting.shouldShowPhysicalInput(context);若应用自定义了 TransformationMethod:PhysicalInputSpan 是临时 span,在 onTextChanged 回调期间存在,之后立即移除,自定义实现可通过检查此 span 区分输入来源。

短信 OTP 保护一、特性背景Android 17 对 targetSdk >= 37 的应用引入 OTP 短信过滤:含 OTP 的短信只有受信任应用能通过 SMS_RECEIVED 广播接收;其他应用需迁移到 SMS Retriever API 或 SMS User Consent API。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37 CompatChangeFILTER_GENERIC_OTP(ID: 437043173)

三、特性内容系统对 OTP 短信使用双 broadcast 机制:向 targetSdk < 37 的应用维持旧行为,向 targetSdk >= 37 的应用加 FILTER_GENERIC_OTP 约束,只有受信任应用才能接收。受信任应用条件(满足任一即可接收 OTP 广播):系统应用、默认短信/助手/拨号应用(ROLE_SMS 等)、具有运营商特权的应用、通过 CompanionDeviceManager 关联的应用、或被授予 READ_OTP_SMS AppOp 的应用。普通第三方应用无法获得 RECEIVE_SENSITIVE_NOTIFICATIONS 权限。

四、应用适配受影响场景应用通过 SMS_RECEIVED 广播自行解析短信内容来提取 OTP 验证码,且 targetSdk >= 37。检测方式搜索代码中 SMS_RECEIVED、SMS_DELIVER 广播接收,以及解析短信内容提取数字验证码的逻辑。迁移方案一:SMS Retriever API(推荐,无需任何 SMS 权限)

// 启动 SMS Retriever,有效期 5 分钟 SmsRetrieverClient client = SmsRetriever.getClient(context); Task task = client.startSmsRetriever(); task.addOnSuccessListener(aVoid -> { // 注册 BroadcastReceiver 等待系统回调 }); // BroadcastReceiver 中接收 OTP 短信 String message = intent.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE); // 从 message 中提取 OTP

SMS Retriever API 要求短信末尾嵌入应用签名 Hash(格式:<#> Your OTP is 123456\nAB12CD34EF5)。迁移方案二:SMS User Consent API(无需修改短信格式)

// 展示系统弹窗,让用户确认分享短信SmsUserConsentClient client = SmsRetriever.getSmsUserConsentClient(context); Task task = client.startSmsUserConsent(/* senderPhoneNumber */ null); task.addOnSuccessListener(aVoid -> { // 注册 BroadcastReceiver 接收 SEND_PERMISSION 结果 });

安全活动安全性增强(BAL 强化)Google 官方文档:行为变更:targetSdk 37+ - 活动安全性一、特性背景Android 17 在 BAL(后台 Activity 启动)方面有三项变化:(1)旧 BAL 常量 @Deprecated,三个新常量成为稳定 API;(2)IntentSender.sendIntent() 不再自动豁免 BAL 检查(targetSdk >= 37);(3)StrictMode BAL 检测对 targetSdk > 35 自动启用。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求编译警告:所有应用;IntentSender BAL 限制:targetSdk >= 37CompatChangeCOVER_INTENT_SENDER(ID: 405995292)三、特性内容

MODE_BACKGROUND_ACTIVITY_START_ALLOWED 废弃:新增三个替代常量(已成为稳定 API):MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS(3)MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE(4)——推荐MODE_BACKGROUND_ACTIVITY_START_DENIED(2)IntentSender BAL 限制:CompatChange COVER_INTENT_SENDER(ID: 405995292),targetSdk >= 37 且 Flag 开启时,IntentSender.sendIntent() 不再自动豁免 BAL 检查StrictMode 自动检测:detectBlockedBackgroundActivityLaunch() 对 targetSdk > 35 的应用在 detectAll() 中自动启用四、应用适配场景 1:替换废弃常量(编译警告,低风险)

// 旧代码(编译警告) ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) // 推荐:大多数场景使用 ALLOW_IF_VISIBLE(仅应用可见时允许启动) ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)场景 2:通过 IntentSender 启动 Activity(targetSdk > 36,高风险)当 bal_cover_intent_sender Flag 开启时,IntentSender.sendIntent() 不再自动豁免 BAL 检查,若调用方无 BAL 特权,启动将被阻断。修复方案:

// 旧代码(在 targetSdk > 36 时可能被阻断) intentSender.sendIntent(context, 0, intent, onFinished, handler); // 新代码:显式指定 BAL 模式 Bundle options = ActivityOptions.makeBasic() .setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE) .toBundle(); intentSender.sendIntent(context, 0, intent, null, options, executor, onFinished);场景 3:使用 StrictMode 检测 BAL 问题(targetSdk > 35 时 detectAll() 自动包含):

StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectAll() // Android 17 对 targetSdk > 35 自动包含 BAL 检测 .penaltyLog() .build());CT 证书透明度默认启用Google 官方文档:行为变更:targetSdk 37+ - 默认启用 CT一、特性背景CT(证书透明度)要求 CA 将证书记录到公开日志。Android 16 默认关闭,Android 17 对 targetSdk >= 37 的应用默认开启。服务端证书若未提交至 CT 日志,HTTPS 握手将以 SSLHandshakeException 失败。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37 当前状态双重门控:CompatChange + Aconfig flag 同时满足才启用

三、特性内容CT 通过双重门控启用:CompatChange DEFAULT_ENABLE_CERTIFICATE_TRANSPARENCY(ID: 407952621,targetSdk > 36)+ Aconfig flag certificate_transparency_default_enabled 同时满足时生效。自动禁用 CT 的场景:NSC 中使用 src="user"(用户证书)或内联证书(src="@raw/xxx")时,CT 自动关闭;连接 localhost 时同样自动跳过。

四、应用适配崩溃特征

javax.net.ssl.SSLHandshakeException: Certificate transparency verification failed at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(...) at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(...)

区分方式:错误消息含 certificate transparency,且仅在 targetSdk >= 37 的设备上复现。不受影响:localhost、NSC 中使用 src="user" 或内联证书、显式声明 、targetSdk <= 36。修复方案方案一(推荐):确保服务端证书已提交 CT 日志(主流 CA 默认支持)。方案二:NSC 中对特定域名关闭 CT:

internal.company.com 方案三:NSC 中使用 src="user" 信任用户证书库(CT 自动禁用)。

加载可写原生库抛 UnsatisfiedLinkError(原生 DCL 加强)Google 官方文档:行为变更:targetSdk 37+ - 更安全的原生 DCL一、特性背景Android 17 将 Safer DCL 保护扩展到原生库:通过 System.load() 加载可写的 .so 文件时将抛出 UnsatisfiedLinkError(targetSdk >= 37,双重开关控制)。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求targetSdk >= 37 三、特性内容Android 17 在 Runtime.load0() 中新增可写性检测:加载 .so 文件前检查 file.canWrite(),双重开关(ART 服务端 flag + CompatChange THROW_ERROR_FOR_WRITABLE_DCL)同时满足时抛出 UnsatisfiedLinkError。UID 豁免:root(0)、system(1000)、adb shell(2000) 跳过检查;只读文件系统上的文件也跳过检查。

四、应用适配触发条件(全部满足才崩溃)

ART 服务端 flag read_only_dynamic_code_load_throw_exception 已开启应用 targetSdkVersion >= 37加载的 .so 文件在文件系统上可写(file.canWrite() == true)运行进程 UID 不是 root(0)、system(1000)、adb shell(2000)文件所在文件系统不是只读挂载崩溃特征

java.lang.UnsatisfiedLinkError: Attempt to load writable file: /data/app/com.example/.../libfoo.so at java.lang.Runtime.load0(Runtime.java:958) at java.lang.System.load(System.java:xxx)

高风险场景

场景原因动态下载并加载插件 .so下载后未设置文件为只读热更新框架直接写入并加载 .so覆盖写入后文件可写从 /data/local/tmp 加载调试库该目录文件默认可写修复方案:加载前将文件设为只读

File soFile = new File(soPath);soFile.setWritable(false, false); // 所有用户均不可写System.load(soFile.getAbsolutePath());

或使用 POSIX 权限:

Os.chmod(soPath, 0444);System.load(soPath);

设备类型大屏设备忽略屏幕方向和尺寸调整限制Google 官方文档:行为变更:targetSdk 37+ - 大屏设备忽略限制 | 详细指南一、特性背景Android 16 已对大屏(sw >= 600dp)上 targetSdk >= 36 的应用强制忽略方向声明,但允许 opt-out。Android 17 封堵 opt-out:targetSdk >= 37 时 PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY 完全失效,且相关 aconfig flag 已删除(行为无条件生效)。游戏类应用(appCategory="game")始终豁免。二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求方向忽略:targetSdk >= 36;opt-out 封堵:targetSdk >= 37当前状态已生效(相关 flag 已删除,行为无条件生效)

三、特性内容Android 17 的大屏策略分两层:

大屏方向忽略(targetSdk >= 36):sw >= 600dp 的设备无条件忽略方向请求封堵 opt-out(targetSdk >= 37):PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY 属性完全失效游戏豁免:声明 appCategory="game" 的应用始终豁免四、应用适配受影响条件:sw >= 600dp 设备 + targetSdk >= 36 + 声明了被忽略的方向/尺寸属性(screenOrientation portrait/landscape 类值、resizableActivity=false、minAspectRatio/maxAspectRatio、setRequestedOrientation() API)。Android 17 额外影响:targetSdk >= 37 时 PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY opt-out 完全失效。不受影响:游戏类应用(appCategory="game")、手机形态设备(sw < 600dp)。视觉问题特征:宽幅黑边(方向被忽略)、布局拉伸(无自适应布局)、方向死循环(反复调用 setRequestedOrientation() 被忽略)。长期适配方案(推荐)

// 3. 响应方向变化而非阻止 @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { // 适配横屏布局 } }游戏应用豁免(唯一 manifest 级豁免手段):

4.新功能和 API本章节介绍 Android 17 引入的新功能和 API,供开发者主动集成使用,不属于强制性行为变更。涉及特性均由 aconfig flag 动态控制,flag 开启后应用可获得相应能力。

核心功能AlarmManager 精确闹钟 OnAlarmListener 变体一、特性背景Android 17 为 AlarmManager.setExactAndAllowWhileIdle() 新增公开的 OnAlarmListener 重载,无需 SCHEDULE_EXACT_ALARM 权限,在 Doze 模式下以独立的触发配额执行回调,适合运行前台服务的长期运行应用。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求无要求(纯新增 API)权限要求不需要 SCHEDULE_EXACT_ALARM当前状态@FlaggedApi,flag 默认关闭三、特性内容新增方法:setExactAndAllowWhileIdle(int type, long triggerAtMillis, String tag, Executor executor, OnAlarmListener listener)。关键差异:无需权限、进程内直接回调、但绑定到调用进程(进程进入 cached 状态时闹钟会被丢弃)。

四、应用适配适合场景:即时通讯心跳、媒体精确触发、在前台服务运行期间使用,无需 SCHEDULE_EXACT_ALARM 权限。使用示例

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);Executor executor = Executors.newSingleThreadExecutor(); // 必须保存引用,以便后续取消 AlarmManager.OnAlarmListener heartbeatListener = () -> { // 在 executor 线程执行,进程内直接回调 doHeartbeat(); }; alarmManager.setExactAndAllowWhileIdle( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + INTERVAL_MS, "MyApp:heartbeat", // tag:必须非空,用于调试和电量归因 executor, heartbeatListener); // 在组件销毁时取消,防止内存泄漏 // alarmManager.cancel(heartbeatListener);

注意:

必须保持进程活跃(前台服务)进程进入 cached 状态后闹钟自动丢弃销毁时调用 cancel(listener) 避免泄漏。

时区偏移量变化广播(ACTION_TIMEZONE_OFFSET_CHANGED)一、特性背景Android 17 新增 ACTION_TIMEZONE_OFFSET_CHANGED 广播,在 DST 切换(时区 ID 不变但 UTC 偏移量变化)时触发,携带新旧偏移量。此前系统没有针对 DST 切换的专用广播。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求无限制(所有应用均可注册接收)当前状态@FlaggedApi,受保护广播

三、特性内容新增广播 ACTION_TIMEZONE_OFFSET_CHANGED(受保护广播),携带两个附加字段:EXTRA_NEW_TIMEZONE_OFFSET(新偏移量,秒)和 EXTRA_OLD_TIMEZONE_OFFSET(旧偏移量,秒)。系统通过 TimeZoneOffsetHelper 预调度一个 RTC 精确闹钟,在 DST 过渡时刻到来时触发该广播各场景广播触发情况:

场景ACTION_TIMEZONE_CHANGEDACTION_TIMEZONE_OFFSET_CHANGED用户切换时区(ID 改变)触发不触发DST 夏令时/冬令时切换(ID 不变)不触发触发Android 16 中 DST 切换不触发不存在

四、应用适配现有代码无需修改:ACTION_TIMEZONE_CHANGED 行为不变。日历/闹钟/调度类应用可主动感知 DST 过渡:

// 注册接收 DST 偏移变化广播 IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_OFFSET_CHANGED); registerReceiver(receiver, filter); // 在 BroadcastReceiver 中处理 int oldOffset = intent.getIntExtra(Intent.EXTRA_OLD_TIMEZONE_OFFSET, 0);// 单位:秒 int newOffset = intent.getIntExtra(Intent.EXTRA_NEW_TIMEZONE_OFFSET, 0); // 单位:秒// 重新计算/刷新时区相关 UI

兼容注意:该广播由 aconfig flag 控制,不保证所有 Android 17 设备均触发。对于时间准确性要求高的场景,建议额外维持 AlarmManager 定时校准作为备选方案。

隐私权联系人选择器(Contact Picker)Google 官方文档:联系人选择器一、特性背景Android 17 引入系统级联系人选择器,应用通过 Intent.ACTION_PICK_CONTACTS 让用户选择要分享的联系人,仅获得一次性会话 URI 权限,无需 READ_CONTACTS 权限。二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求无限制(新功能)核心类ContactsPickerSessionContract当前状态由服务端 flag 动态控制

三、特性内容核心 API(ContactsPickerSessionContract):

API说明ACTION_PICK_CONTACTS启动联系人选择器的 Intent ActionEXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS必填:指定需要的联系人数据字段(MIME 类型列表,如 Phone.CONTENT_ITEM_TYPE、Email.CONTENT_ITEM_TYPE 等)EXTRA_PICK_CONTACTS_MATCH_ALL_DATA_FIELDS可选:要求联系人同时具备所有指定字段(默认 false)EXTRA_PICK_CONTACTS_SELECTION_LIMIT可选:最多可选联系人数量(默认 50,最大 100)CONTENT_URI会话结果 URI 基础路径四、应用适配方案一(推荐):迁移到新 API,消除 READ_CONTACTS 权限依赖

// 1. 构建 Intent val requestedFields = arrayListOf( ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ) val pickIntent = Intent(ContactsPickerSessionContract.ACTION_PICK_CONTACTS).apply { putStringArrayListExtra( ContactsPickerSessionContract.EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS, requestedFields ) // 可选:允许多选 putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 可选:限制最多选 10 个 putExtra(ContactsPickerSessionContract.EXTRA_PICK_CONTACTS_SELECTION_LIMIT, 10) } // 2. 启动(推荐使用 ActivityResultLauncher) val launcher = registerForActivityResult(StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { val sessionUri = result.data?.data ?: return@registerForActivityResult // 3. 用 ContentResolver 查询会话 URI(在后台线程执行) contentResolver.query(sessionUri, null, null, null, null)?.use { cursor -> while (cursor.moveToNext()) { val mimeType = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)) // 根据 mimeType 处理对应字段 } } } } launcher.launch(pickIntent)注意事项

无需 READ_CONTACTS 权限,选择器通过会话 URI 授予一次性访问权限结果 URI 为临时会话 URI,进程终止后失效,需立即持久化数据SELECTION_LIMIT 超过 100 会抛 IllegalArgumentExceptionflag 未开启时 ACTION_PICK_CONTACTS 无法解析,应做好回退处理兼容测试:targetSdk < 37 的应用可通过 putExtra(Intent.EXTRA_USE_SYSTEM_CONTACTS_PICKER, true) 在 Android 17 设备上测试新选择器行为安全Android 高级保护模式(AAPM)一、特性背景AAPM 是用户可开启的设备级安全加固模式。Android 17 将 AdvancedProtectionManager 核心 API 正式升级为稳定 API,新增 AccessibilityService 限制:AAPM 开启后,非工具型 AccessibilityService 被强制禁用。

维度说明平台版本Android 17(API 37)targetSdk 要求无(主动查询)权限要求QUERY_ADVANCED_PROTECTION_MODE(normal 级别)当前状态核心 API 已稳定三、特性内容公开 API(core/api/current.txt 已收录,需 QUERY_ADVANCED_PROTECTION_MODE normal 权限):

AdvancedProtectionManager.isAdvancedProtectionEnabled():查询当前状态registerAdvancedProtectionCallback(executor, callback):监听状态变化(注册时立即回调一次)unregisterAdvancedProtectionCallback(callback):取消监听AAPM 开启后的影响:禁止侧载安装 APK、限制 USB 数据传输、强制 Play Protect 扫描、非工具型 AccessibilityService 被强制禁用。

四、应用适配场景一:根据 AAPM 状态调整功能

AdvancedProtectionManager mgr = context.getSystemService(AdvancedProtectionManager.class); if (mgr.isAdvancedProtectionEnabled()) { disableRiskyFeatures(); } // 监听状态变化(避免轮询) mgr.registerAdvancedProtectionCallback(mainExecutor, enabled -> { if (enabled) disableRiskyFeatures(); else enableRiskyFeatures(); });场景二:AccessibilityService 应用(重点适配项)若应用的 AccessibilityService 是辅助功能工具,必须声明 isAccessibilityTool="true",否则 AAPM 模式下服务将被强制关闭:

PQC APK 混合签名(ML-DSA)一、特性背景Android 17 引入 APK V3.2 签名方案(Hybrid Block),支持经典密钥(RSA/EC)与 ML-DSA(PQC)配对混合签名;旧版设备仅验证经典签名块,Android 17+ 设备同时验证 Hybrid 块;使用 Google Play 签名的应用无需操作,自行管理密钥的应用需更新。

二、适用范围

维度说明平台版本Android 17(API 37)targetSdk 要求无(签名方案对所有应用适用)查询)权限要求仅影响自管理签名密钥的应用当前状态@FlaggedApi三、特性内容Android 17 新增 APK V3.2 签名方案(Hybrid Block,签名块 ID 0x70e1c89f,最低 SDK = API 37),支持 ML-DSA-65(推荐)和 ML-DSA-87 两种 PQC 变体。AndroidKeyStore 同步新增 KEY_ALGORITHM_ML_DSA / KEY_ALGORITHM_ML_DSA_65 / KEY_ALGORITHM_ML_DSA_87 常量。apksigner 命令行新增参数:

# --hybrid-signer-role classical —— 指定该签名者为 hybrid 中的经典密钥# --hybrid-signer-role pqc —— 指定该签名者为 hybrid 中的 ML-DSA 密钥# --hybrid-min-sdk-version —— hybrid 块生效的最低 SDK 版本(默认 37)注意:V4 签名当前不支持 PQC 签名。

四、应用适配使用 Google Play 签名(Play App Signing)的应用无需任何操作。Google Play 将自动为应用添加 PQC 混合签名,开发者只需等待 Play 推出相关选项。使用自行管理签名密钥的应用

1.生成 ML-DSA 密钥对:使用更新后的 Android build 工具(apksigner)或 Java KeyPairGenerator 生成 ML-DSA-65 密钥对及自签名证书

2.更新签名命令:

apksigner sign \ --ks keystore.jks --ks-key-alias classical_key \ --hybrid-signer-role classical \ --next-signer \ --ks keystore.jks --ks-key-alias mldsa65_key \ --hybrid-signer-role pqc \ --hybrid-min-sdk-version 37 \ output.apk

3.密钥约束:必须创建新的经典密钥,不能复用旧的经典密钥

4.向后兼容:旧版 Android 设备仍验证 V3.0/V3.1 经典签名

Android 开发者验证(Developer Verification)Google 官方文档:Android 开发者验证一、特性背景Android 17 引入开发者验证(Developer Verification)框架,要求所有应用都必须由经过验证的开发者注册,用户才能在已获认证的Android设备上安装。验证逻辑由设备上的验证代理(如 Google Play Services)实现。Google 计划 2026 年 9 月起在部分地区强制执行,2027 年后全球推广。关键信息: Google Play 分发的应用 98% 自动注册;ADB 安装始终绕过;企业托管设备豁免;未注册应用侧载需经过高级确认流程。

二、适用范围

维度说明平台版本Android 17(API 37)引入框架targetSdk 要求无(由验证代理策略决定)当前状态框架已就绪,实际执行取决于设备上的验证代理三、特性内容Android 17 新增 Developer Verification 子系统(core/java/android/content/pm/verify/developer/,全新目录),采用可插拔的 Verification Agent 架构:

核心流程:应用安装时,PackageInstaller 调用设备上的验证代理(如 Google Play Services),验证代理查询开发者注册库、校验签名密钥,返回验证结果ADB 安装始终绕过验证:(用于开发/测试)默认验证超时:10 秒(可配置),最大延长 10 分钟安装失败时,PackageInstaller 会根据验证结果向用户展示对应提示(开发者未通过验证、网络不可用、验证不可用等)。新增安装失败原因常量:DEVELOPER_VERIFICATION_FAILED_REASON_UNKNOWN(0)、DEVELOPER_VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE(1)、DEVELOPER_VERIFICATION_FAILED_REASON_DEVELOPER_BLOCKED(2)。

四、应用适配受影响场景通过非 Google Play 渠道分发的应用(APK 直接下载、企业内部分发、第三方应用商店),在认证 Android 设备上安装时,若开发者未注册且设备策略启用验证,安装将被阻止或弹出警告提示。不受影响:Google Play 安装、ADB 安装(开发调试)、企业托管设备、策略为 POLICY_NONE 的设备。适配建议第一步:确认分发渠道

仅 Google Play 分发:无需操作(98% 应用自动注册)Google Play + 其他渠道:在 Google Play 管理中心 确认注册状态仅非 Play 渠道:需在 Android Developer Console 注册第二步:注册应用(非 Play 渠道开发者)

访问 Android Developer Console 创建账号完成身份验证(个人开发者需政府签发的身份证明;组织开发者额外需要 DUNS 号码)注册应用包名(applicationId)和 APK 签名证书的 SHA-256 指纹通过签名验证证明包名所有权(使用注册密钥签署包含指定代码片段的 APK)第三步(面向安装器应用):处理安装结果中新增的 EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON Extra(DEVELOPER_BLOCKED、NETWORK_UNAVAILABLE、UNKNOWN 三种原因)。时间线

时间事件2026 年 3 月注册开放(Full Distribution)2026 年 6 月Limited Distribution 早期注册2026 年 9 月部分地区强制执行(巴西、印尼、新加坡、泰国)2027 年 +全球推广

5.迁移指南每次发布新的 Android 版本时,我们都会推出一些全新的功能并引入一些行为变更,目的就在于提高 Android 的实用性、安全性和性能。在许多情况下,您的应用都可以直接使用并完全按预期运行;而在其他的一些情况下,您可能需要对应用进行更新以适应这些平台变更。源代码发布到 AOSP(Android 开源项目)后,用户随之就可能开始使用新平台。因此,应用必须做好准备,让用户能够正常使用,最好还能利用新的功能和 API 发挥新平台的最大优势。典型的迁移包含两个阶段,这两个阶段可以同时进行:

确保应用兼容性(在 Android 17 最终发布前)针对新平台的功能和 API 调整应用(最终发布后尽快进行)

确保与 Android 17 兼容您必须测试现有应用在 Android 17 上的运行情况,以确保更新到最新版 Android 的用户获得良好的体验。有些平台变更可能会影响应用的行为方式,因此,必须尽早进行全面测试并对应用进行任何必要的调整。您通常可以调整应用并发布更新,而无需更改应用的 targetSdkVersion。同样,您应该也不需要使用新的 API 或更改应用的 compileSdkVersion,不过这一点可能要取决于应用的构建方式及其所使用的平台功能。在开始测试之前,请务必熟悉适用于所有应用的行为变更。即使您不更改应用的 targetSdkVersion,这些变更也可能会影响您的应用。

测试应用在 Android 17 上的兼容性在大多数情况下,测试与 Android 17 的兼容性与普通的应用测试类似。这时有必要回顾一下核心应用质量指南和测试最佳实践。如要进行测试,请在搭载 Android 17 的设备上安装您当前发布的应用,并完成所有流程和功能,以排查问题。为帮助您确定测试重点,请查看 Android 17 中引入的适用于所有应用的行为变更,这些变更会影响应用的功能或导致应用崩溃。此外,请务必查看并测试受限非 SDK 接口的使用。您应使用应用公共 SDK 或 NDK 等效项替换应用使用的任何受限接口。留意突出显示这些访问权限的 logcat 警告,并使用 StrictMode 方法 detectNonSdkApiUsage() 以程序化地捕获它们。最后,请务必完整测试应用中的库和 SDK,确保它们在 Android 17 上按预期运行,并遵循隐私权、性能、用户体验、数据处理和权限方面的最佳实践。如果您遇到问题,请尝试更新到最新版本的 SDK,或联系 SDK 开发者寻求帮助。当您完成测试并进行更新后,我们建议您立即发布兼容的应用。这样可以尽早让您的用户测试应用,并帮助用户顺利过渡到 Android 17。

更新应用的目标平台并使用新 API 进行构建发布应用的兼容版本后,下一步是通过更新 targetSdkVersion 并利用 Android 17 的新 API 和功能来添加对 Android 17 的全面支持。准备就绪后,您即可开始进行这些更新,请注意以新平台为目标平台的 Google Play 要求。当您计划全面支持 Android 17 时,请查看影响以 Android 17 为目标平台的应用的行为变更。这些针对性的行为变更可能会导致需要解决的功能问题。在某些情况下,这些变更需要进行大量开发工作,因此我们建议您尽早了解并解决这些问题。为帮助确定影响您的应用的具体行为变更,请使用兼容性切换开关来测试已启用所选变更的应用。以下步骤介绍了如何全面支持 Android 17。

获取 SDK,更改目标平台,使用新 API 进行构建如需开始针对 Android 17 全面支持进行测试,请使用最新预览版的 Android Studio 下载 Android 17 SDK,以及所需的任何其他工具。接下来,更新应用的 targetSdkVersion 和 compileSdkVersion,然后重新编译应用。如需了解详情,请参阅 SDK 设置指南。

测试 Android 17 应用编译应用并将其安装到搭载 Android 17 的设备上后,请开始测试,确保应用能够在 Android 17 上正常运行。某些行为变更仅在应用以新平台为目标平台时才适用,因此您需要在开始之前查看这些变更。与基本兼容性测试一样,完成所有流程和功能以查找问题。将测试重点放在以 Android 17 为目标平台的应用的行为变更上。您还可以根据核心应用质量指南和测试最佳实践检查您的应用。请务必查看并测试可能适用的受限非 SDK 接口的使用。留意突出显示这些访问权限的 logcat 警告,并使用 StrictMode 方法 detectNonSdkApiUsage() 以编程方式捕获它们。最后,请务必完整测试应用中的库和 SDK,确保它们在 Android 17 上按预期运行,并遵循隐私权、性能、用户体验、数据处理和权限方面的最佳实践。如果您遇到问题,请尝试更新到最新版本的 SDK,或联系 SDK 开发者寻求帮助。

使用应用兼容性切换开关进行测试Android 17 包含兼容性切换开关,可让您更轻松地在应用中测试针对性的行为变更。对于可调试的应用,切换开关可让您:

在不实际更改应用的 targetSdkVersion 的情况下测试针对性的变更。您可以使用切换开关强制启用特定的针对性行为变更,以评估对现有应用的影响。仅针对特定变更进行测试。您可以使用切换开关停用除要测试的变更之外的所有针对性变更,而不必一次处理所有针对性变更。通过 adb 管理切换开关。您可以使用 adb 命令在自动测试环境中启用和停用可切换的变更。使用标准变更 ID 更快地进行调试。每个可切换的变更都具有唯一 ID 和名称,可用于在日志输出中快速调试根本原因。在您准备更改应用的目标平台时,或者在您积极开发以便支持 Android 17 时,切换开关将十分有用。如需了解详情,请参阅兼容性框架变更 (Android 17)。

6.开发者支持若您在适配过程中遇到任何问题,都可以通过参考此文档反馈给我们,我们的兼容性工程师团队会尽快为您解决。联系我们

相关推荐

16进制转10进制 - 十六进制转换十进制计算器在线工具,支持浮点数
向四周看 的词语(用什么成语形容向四周看)(7个)
365商城官网

向四周看 的词语(用什么成语形容向四周看)(7个)

📅 11-02 👁️ 9048
2025中国科技机器人企业TOP50
365充值真人注册

2025中国科技机器人企业TOP50

📅 12-30 👁️ 5711