Android 17 开发者指南:新 API、重大变更以及现在应该迁移的内容

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 APISMS 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 月份的平台稳定性测试做好准备。

相关推荐
LcGero3 小时前
Cocos Creator 业务与原生通信详解
android·ios·cocos creator·游戏开发·jsb
fundoit3 小时前
MySQL插入数据遇到唯一键已存在怎么办?
android·数据库·mysql
ameyume3 小时前
基于原生Android 16设置音量调用流程
android·audio
ii_best3 小时前
lua语言开发脚本基础、mql命令库开发、安卓/ios基础开发教程,按键精灵新手工具
android·ios·自动化·编辑器
BoomHe1 天前
Android AOSP13 原生 Launcher3 壁纸获取方式
android
Digitally1 天前
如何将联系人从 Android 转移到 Android
android
李小枫1 天前
webflux接收application/x-www-form-urlencoded参数
android·java·开发语言
爱丽_1 天前
MySQL `EXPLAIN`:看懂执行计划、判断索引是否生效与排错套路
android·数据库·mysql
NPE~1 天前
[App逆向]环境搭建下篇 — — 逆向源码+hook实战
android·javascript·python·教程·逆向·hook·逆向分析