Android 17 正式发布!target 37 一大批旧代码直接不能用了

Android 17 已经正式发布,对应 API level 37。

target SDK 37 以后,有几类旧假设会失效:大屏不能继续锁方向和比例,部分配置变化默认不再重建 Activity,本地网络访问要走新权限或系统选择器,一些运行时反射写法也会直接失败。

如果项目准备升级 targetSdk,下面这些点要先进兼容性验证。

大屏限制

Android 17 继续推进大屏和桌面模式。对 target API 37 的 App,在 sw >= 600dp 的大屏设备上,系统会忽略一批旧限制,包括 screenOrientationsetRequestedOrientation()resizeableActivity=false,以及 minAspectRatio / maxAspectRatio 这类比例约束。游戏按 Google Play 的 app category 仍有豁免。

这意味着很多"强制竖屏"的业务页面,在平板、折叠屏、桌面窗口里要按可变窗口处理。以前 Manifest 写法能兜住,现在系统会把窗口尺寸和姿态交还给用户。

旧代码里常见的写法大概是这样:

bash 复制代码
<activity
    android:name=".MainActivity"
    android:screenOrientation="portrait"
    android:resizeableActivity="false" />

升级 target 37 后,页面要在窄屏、宽屏、分屏、自由窗口下正常使用。登录、支付、相机预览、地图、播放器这些页面,最容易暴露布局假设。

Android 17 还加入了 App Bubbles、Bubble Bar 和桌面交互式 PiP。用户可以把 App 变成浮动 bubble,大屏任务栏会管理这些 bubble;桌面 PiP 也不再只是只读小窗,而是保留交互。页面最小宽高、焦点、输入法、返回栈,都要按真实窗口状态检查。

Activity 重建

Android 17 改了部分配置变化的默认处理方式。对一些不需要完整 UI 重绘的 configuration change,系统默认不再重启 Activity,而是把变化发给 onConfigurationChanged()

原文里列到的包括 CONFIG_KEYBOARDCONFIG_KEYBOARD_HIDDENCONFIG_NAVIGATIONCONFIG_TOUCHSCREENCONFIG_COLOR_MODE。如果项目以前依赖 Activity 重建来重新读取资源、重建 UI 或刷新状态,这里会有行为差异。

需要完整重建时,Manifest 里可以显式声明新的 android:recreateOnConfigChanges

bash 复制代码
<activity
    android:name=".EditorActivity"
    android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation" />

更稳的做法是把这类配置变化当成普通状态更新处理。比如外接键盘、触控板、导航设备变化时,页面不要默认假设自己会走一遍 onCreate()

bash 复制代码
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    updateInputMode(newConfig.keyboard, newConfig.navigation)
}

这个点很适合用 Android 17 模拟器配合外接键盘、鼠标、触控板测试。只点页面流程不够,要看设备输入形态变化后,快捷键、焦点和输入框状态有没有丢。

Continue On

Android 17 加了 Continue On,用来把任务从手机切到平板等设备上继续。用户在平板任务栏里看到最近在手机上打开的 App 建议,点击后可以打开 App,并进入上一次任务位置;也支持 app-to-web 的回退。

App 侧入口比较直接:在 Activity 里启用 handoff,然后提供要传递的数据。

bash 复制代码
class MyHandoffActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHandoffEnabled(true, null)
    }

    override fun onHandoffActivityDataRequested(
        handoffRequestInfo: HandoffActivityDataRequestInfo
    ): HandoffActivityData {
        return createHandoffData()
    }
}

Continue On 要处理的是"继续"的粒度。阅读 App 可能是文章 ID 和滚动位置,电商 App 可能是商品详情或购物车,编辑类 App 则要处理草稿保存和账号一致性。

如果任务状态只存在内存里,跨设备就接不上。至少要有稳定的 deep link、服务端状态或本地可同步状态,否则 Continue On 只能打开 App 首页。

Compose 优先

Android 17 发布时,Android 开发文档里明确写到:新的 Android API、库、工具和开发者指南会面向 Jetpack Compose 构建。android.widget 里的 legacy View 组件,以及 Fragment、RecyclerView、ViewPager 这类基于 View 的 Jetpack 库进入维护模式,只保留关键 bug 修复,不再加新功能。

现有 View 项目还能继续维护,真实项目里 View 和 Compose 也会长期共存。实际影响是:新能力、新示例、新适配方案会优先出现在 Compose 生态里。

大屏适配就是一个明显例子。Material 3 Adaptive 里的 NavigationSuiteScaffold 可以根据窗口尺寸切换底部导航和导航栏;Navigation 3 里的 list-detail、supporting pane 也更适合平板和桌面布局。

项目里如果还在大量使用 XML,新增页面、设置页、二级页面、平板专用布局更适合放到 Compose。核心交易链路不需要一次性迁,但新页面继续用旧 View 栈,后面接大屏、桌面、bubble 和 PiP 时会更吃力。

性能限制

Android 17 对性能也有几类变化。

App 内存限制已经单独值得写一篇。超过设备 RAM 对应限制时,系统可以终止进程;退出原因里会看到 ApplicationExitInfo.REASON_OTHER,description 里包含 MemoryLimiter:AnonSwap。这类问题不是普通 Java OOM,Crash 平台可能没有常规堆栈。

target 37 后,android.os.MessageQueue 换成 lock-free 实现,可以减少 missed frames、改善启动和繁忙队列性能。但如果项目通过反射访问 MessageQueue 私有字段或方法,就可能出问题。测试代码需要看 TestLooperManager 新增的 peekWhen()poll(),不要继续依赖内部实现。

还有一个运行时变化更直接:target 37 的 App 不能再修改 static final 字段。反射修改会抛 IllegalAccessException,JNI 里用 SetStatic<Type>Field 修改会让应用崩溃。

bash 复制代码
val field = BuildConfig::class.java.getDeclaredField("DEBUG")
field.isAccessible = true
field.set(null, true) // Android 17 + target 37 会失败

这类写法通常出现在测试 hook、灰度开关、老的反射框架或三方 SDK 里。升级 target 前,先全文搜 setAccessible(true)getDeclaredFieldMessageQueueSetStatic 这类关键词,比等线上异常更靠谱。

隐私权限

Android 17 继续把"拿一类数据"改成"让用户选一次数据"。系统联系人选择器、Photo Picker 纵横比定制、系统渲染的位置按钮、EyeDropper API 都属于这个方向。

EyeDropper 的调用方式比较清楚。它用系统能力让用户从屏幕上取色,不需要 App 自己申请屏幕录制或媒体投影权限。

bash 复制代码
val eyeDropperLauncher =
    registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val color = result.data?.getIntExtra(Intent.EXTRA_COLOR, Color.BLACK)
            applyPickedColor(color)
        }
    }

fun launchColorPicker() {
    eyeDropperLauncher.launch(Intent(Intent.ACTION_OPEN_EYE_DROPPER))
}

本地网络访问也变了。target 37 的 App 访问局域网设备时,要么使用系统中介的设备选择器,要么申请新的 ACCESS_LOCAL_NETWORK 运行时权限。它归在 NEARBY_DEVICES 权限组里,用户已经授权过同组权限时,不一定会再次弹窗。

bash 复制代码
<uses-permission android:name="android.permission.ACCESS_LOCAL_NETWORK" />

智能家居、投屏、打印、局域网调试、设备发现这类功能,要重点检查发现设备、连接设备和权限拒绝后的 UI 状态。以前直接扫局域网的实现,target 37 后要重新验证。

SMS OTP 也有保护。标准短信验证码对大多数 target 37 App 会延迟 3 小时可见,相关广播会被保留,SMS provider 查询会被过滤。依赖读短信提取验证码的实现,要迁到 SMS Retriever 或 SMS User Consent。

动态加载

Android 14 已经对 DEX / JAR 动态加载做了 safer dynamic code loading 限制。Android 17 把这类保护扩展到 native library。

target 37 后,如果通过 System.load() 加载 native 文件,这些文件必须标记为只读,否则系统会抛 UnsatisfiedLinkError

bash 复制代码
val soFile = File(filesDir, "plugin.so")

// 写入完成后,加载前要把文件改成只读
soFile.setReadOnly()
System.load(soFile.absolutePath)

插件化、热修复、算法包下发、动态 native 能力加载,都要查这条链路。只看 APK 自带的 lib/ 目录不够,运行时写入 app 私有目录再加载的 .so 才是风险点。

验证路径

Android 17 兼容性验证不要只跑一遍启动和登录。

先用 Android Studio 安装 Android 17 SDK 和 64-bit Emulator system image,再把现有线上包安装到 Android 17 设备或模拟器上跑一轮。确认没有运行时行为变化后,再切 targetSdk 37 跑同样流程。

重点验证这些路径:

bash 复制代码
大屏 / 折叠屏 / 桌面窗口:
方向、分屏、自由缩放、最小窗口、bubble、PiP

输入设备:
外接键盘、鼠标、触控板、焦点、快捷键

运行时:
MessageQueue 反射、static final 反射、native 动态加载

权限:
局域网设备发现、短信验证码、联系人、位置、取色

性能:
内存退出原因、R8 配置、LeakCanary、ProfilingManager

SDK、库、游戏引擎也要提前处理。下游 App 想升级 target 37 时,如果你的 SDK 还在反射 MessageQueue、读短信、动态加载 native 文件,最后会卡住别人。

最后

Android 17 对开发者最直接的检查点,是 target SDK 37 后的行为变化。

大屏限制、Activity 重建、本地网络权限、SMS OTP、MessageQueuestatic final、native 动态加载,这些都能在代码里找到对应入口。先把这些路径跑通,再看是否接 Continue On、App Bubbles、桌面 PiP 和新的媒体能力。

#Android #Android17 #JetpackCompose #Android开发

相关推荐
Carson带你学Android1 小时前
Android 17 正式发布:AI 终于成了系统能力
android·前端·ai编程
三少爷的鞋2 小时前
当 UseCase 开始长期监听,它可能已经不是 UseCase 了
android
恋猫de小郭15 小时前
Android 限制侧载新进展,谷歌联合国内厂商推验证计划
android·前端·flutter
恋猫de小郭15 小时前
解读 Android 17 全新内存限制,有没有“豁免”后门?
android·前端·flutter
贾艺驰18 小时前
实战Android Framework: 新增一个系统权限
android
alexhilton1 天前
使用Android Archive进行打包
android·kotlin·android jetpack
badhope1 天前
做了几年安卓开发,这些坑我帮你踩过了
android·android studio
逐光老顽童3 天前
Java 与 Kotlin 混合开发避坑指南:30 个真实案例实录
android·kotlin
爱勇宝3 天前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员