Android 17 开发者指南:新 API、重大变更以及现在应该迁移的内容
API 级别 37 带来了自适应布局、无锁消息传递和精细化的隐私控制。以下是对您的应用至关重要的内容。
Android 17(API 级别 37)改变了平台对应用行为的预期。谷歌用持续更新的Android Canary 渠道取代了开发者预览版模式,该渠道会在功能通过内部测试后立即发布,而不是像以前那样按季度发布。除了这种发布模式之外,该平台还强制在大屏幕上使用自适应布局,在底层引入了无锁消息处理机制,添加了专业级相机 API,并通过对联系人、本地网络和短信的精细控制来加强隐私保护。
对于面向新 SDK 的开发者而言,有几项变更需要立即关注。锁定屏幕方向或依赖反射访问框架内部机制的应用将会失效。请求广泛权限的应用READ_CONTACTS必须迁移到作用域限定的替代方案。平台稳定性将于 2026 年 3 月发布,稳定版将在数月后推出。准备时间非常有限。本指南涵盖了影响最大的新增功能和迁移,并提供您可以立即应用的简洁代码片段。
自适应布局在大屏幕上成为标配
面向 SDK 37 且运行在大屏幕(最小尺寸 >= 600dp)上的应用不再支持锁定屏幕方向或宽高比。系统会忽略 <img> screenOrientation、 <img> resizeableActivity、 minAspectRatio<img> 和<img> 等设置maxAspectRatio。您的 Activity 必须在用户选择的任何窗口尺寸下都能正确渲染。
例外情况 :小屏幕(< 600dp)和应用程序将保持不变。在平台稳定性更新之前android:appCategory="game",请在 Pixel Tablet 和 Pixel Fold 模拟器上进行测试。targetSdkPreview = "CinnamonBun"
用配置更改处理和响应式布局替换固定方向的清单文件:
java
// BEFORE (ignored on Android 17 large screens)
<activity android:screenOrientation="portrait" android:resizeableActivity="false" />
// AFTER
<activity android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" />
使用 JetpackWindowSizeClass实现响应式布局:
kotlin
@Composable
fun AdaptiveScreen() {
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
when (windowSizeClass.windowWidthSizeClass) {
WindowWidthSizeClass.COMPACT -> PhoneLayout()
WindowWidthSizeClass.MEDIUM -> TabletLayout()
WindowWidthSizeClass.EXPANDED -> DesktopLayout()
}
}
减少配置更改后的活动重启次数
系统不再为键盘、触摸屏、导航、颜色模式或桌面模式的更改重新创建活动,而是直接onConfigurationChanged()接收这些更新。此前,连接蓝牙键盘或在桌面模式下连接扩展坞会触发系统完全重启,导致视频播放中断和状态丢失。减少重启次数意味着减少输入丢失,带来更流畅的体验。
如果您的应用需要完全重启才能进行特定更改,请明确选择启用:
java
<activity
android:name=".MainActivity"
android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation|touchscreen|colorMode" />
ProfilerManager:在发生故障时捕获数据
Android 17 新增了三个ProfilingManager 触发器,可自动捕获诊断数据:
- TRIGGER_TYPE_COLD_START --- 应用程序冷启动期间的调用堆栈和系统跟踪
- TRIGGER_TYPE_OOM
OutOfMemoryError---发生时进行Java 堆转储 - TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE --- 当系统因 CPU 使用率过高而终止应用程序时,调用堆栈示例
注册监听器并将结果上传到您的分析或崩溃服务。速率限制可防止过度收集:冷启动 24 小时,OOM 1 小时,CPU 崩溃 6 小时。
kotlin
val profilingManager = context.getSystemService(ProfilingManager::class.java)
val coldStartTrigger = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_COLD_START)
.setRateLimitingPeriodHours(24)
.build()
profilingManager.registerForAllProfilingResults(executor) { result ->
when (result.triggerType) {
ProfilingTrigger.TRIGGER_TYPE_COLD_START -> uploadTrace(result)
ProfilingTrigger.TRIGGER_TYPE_OOM -> uploadHeapDump(result)
ProfilingTrigger.TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE -> uploadCpuProfile(result)
}
}
性能:无锁消息队列和 JobDebugInfo
面向 SDK 37 的应用将获得一个无锁MessageQueue实现(DeliQueue),它可以减少主线程上的争用并避免丢帧。但这会破坏任何对私有MessageQueue字段进行反射的代码。请检查您的代码库和依赖项中是否存在此类反射;请改用公共 APIHandler.post或协程。
对于 JobScheduler 调试,新版本getPendingJobReasonStats()只需一次调用即可返回一个包含待处理原因和累计持续时间的映射:
kotlin
val stats = jobScheduler.getPendingJobReasonStats(jobId)
// Returns e.g. PENDING_JOB_REASON_CONSTRAINT_CHARGING -> 60000 (ms)
ART 中的分代垃圾回收优先回收年轻一代,从而减少分配密集型工作负载的 GC 暂停。
联系人选择器:基于会话的访问,而非 READ_CONTACTS
新的联系人选择器(ACTION_PICK_CONTACTS)仅授予用户对所选联系人的临时访问权限。无需广泛的READ_CONTACTS权限。
kotlin
val pickerLauncher = registerForActivityResult(
ActivityResultContracts.PickContact()
) { uri ->
uri?.let {
context.contentResolver.query(it, arrayOf(Contacts.DISPLAY_NAME), null, null, null)
?.use { cursor ->
if (cursor.moveToFirst()) processContact(cursor.getString(0))
}
}
}
// Launch picker
pickerLauncher.launch(null)
会话结束后,访问权限将自动撤销。
切换 API:在另一设备上恢复活动
Handoff API允许用户在一台 Android 设备上启动一个 Activity,然后在另一台设备上继续执行。可以为每个 Activity 启用此功能:
kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHandoffEnabled(true)
}
override fun onHandoffActivityRequested(): HandoffActivityData {
return HandoffActivityData.Builder()
.setDeepLink("myapp://article/$currentArticleId")
.setExtras(Bundle().apply { putInt("scroll_position", scrollY) })
.build()
}
附近的设备会在启动器和任务栏中显示可用的活动。
高级保护模式集成
高级保护模式 (AAPM)允许高风险用户选择更严格的安全设置。检测到该模式后,请调整您的应用行为:
kotlin
// AndroidManifest: <uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" />
val apm = context.getSystemService(AdvancedProtectionManager::class.java)
if (apm?.isAdvancedProtectionEnabled() == true) {
enforceHardenedPolicy() // e.g., disable WebView JS, enable pinning
}
apm?.registerAdvancedProtectionCallback(executor) { enabled ->
if (enabled) enforceHardenedPolicy() else restoreStandardPolicy()
}
实时更新语义颜色和吸管 API
实时更新通知支持语义颜色注释。无需自定义布局逻辑,即可为文本应用样式,使通知传达含义(蓝色表示信息,绿色表示安全,橙色表示警告,红色表示危险):
kotlin
val ssb = SpannableStringBuilder()
.append("Status: ")
.append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
.append(", ")
.append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
Notification.Builder(context, channelId)
.setContentText(ssb)
.setOngoing(true)
.build()
EyeDropper API 允许应用程序在无需屏幕截图权限的情况下从屏幕上采样像素颜色。设计工具、颜色选择器和辅助功能无需MediaProjection任何权限即可获得颜色选择功能CAPTURE_VIDEO_OUTPUT。
相机和媒体升级
动态输出更新 ------CameraCaptureSession.updateOutputConfigurations()允许您在不关闭会话的情况下切换照片和视频输出。这消除了模式切换期间出现的 200-500 毫秒的卡顿现象:
kotlin
fun switchToVideo(session: CameraCaptureSession, videoOutput: OutputConfiguration) {
session.updateOutputConfigurations(listOf(previewOutput, videoOutput))
}
VVC (H.266) 支持 --- 平台支持多功能视频编码 (VVC),压缩率比 HEVC 高 50%。恒定质量模式 ---MediaRecorder.setVideoEncodingQuality()启用 CQ 模式,可在不同场景下实现更一致的视觉质量。后台音频强化--- 当应用缺少有效的前台生命周期状态时,播放、音频焦点和音量更改将静默失败。
数据计划 API ---SubscriptionInfo.getStreamingAppMaxDownlinkKbps()并getStreamingAppMaxUplinkKbps()允许流媒体应用查询运营商分配的带宽限制,从而根据实际计划约束实现自适应的质量和比特率选择。
气泡窗口 ------长按任意启动器图标即可创建一个运行完整应用的浮动窗口。在大屏幕上,任务栏中的气泡栏可以管理多个浮动窗口。请遵循多窗口指南,以确保您的应用在以气泡形式运行时能够正常工作。
在升级到 SDK 37 之前必须执行的迁移
明文流量 ------android:usesCleartextTraffic在 SDK 37 中已弃用。应用程序默认阻止明文流量。请添加网络安全配置文件,并仅允许需要明文流量的域使用明文。此配置文件可domain-config用于例外情况和debug-overrides本地开发。
本地网络访问权限 ------新的ACCESS_LOCAL_NETWORK运行时权限(在NEARBY_DEVICES组中)控制局域网发现和通信。请在扫描设备前请求此权限。隐私保护设备选择器允许用户授权特定设备,而不是授予全局网络访问权限。
短信验证码 (OTP) --- Android 17 将大多数应用的程序化 OTP 访问延迟三小时,以降低劫持风险。默认短信应用和已获批准的配套应用不受此限制。请迁移到SMS Retriever API或SMS User Consent API来处理 OTP。
无锁消息队列 --- 面向 SDK 37 的应用将获得无锁消息队列MessageQueue,这可以提升性能,但会破坏对私有字段的反射。第三方性能分析工具和与消息队列内部交互的自定义 Looper 实现将无法正常工作。请检查您的代码库和依赖项中是否存在MessageQueue反射。
自定义通知 ------自定义通知的尺寸有严格的限制RemoteViews。过大的布局可能会被拒绝或截断。请尽可能使用标准通知模板。
NPU 访问 ------面向 SDK 37 且直接访问神经处理单元 (NPU) 的应用必须FEATURE_NEURAL_PROCESSING_UNIT在清单文件中声明此硬件特性。这有助于明确哪些应用依赖于 NPU 功能,从而进行安装筛选。
链接:
Android 17 鼓励应用适配各种屏幕尺寸、严格遵守权限控制,并避免依赖平台内部机制。强制在大屏幕上使用响应式布局,加上无锁消息功能和更严格的隐私控制,表明该平台期望应用在各种设备尺寸上都能达到生产级品质。请在Pixel Tablet 和 Fold 模拟器上进行测试,尽早修复清单和反射问题,并为 3 月份的平台稳定性测试做好准备。