Android 15/16 运行动画时 Logcat 大量输出帧率日志,如何解决?
仅凭这些日志无法确定动画计算出错。Compose 官方源码已有针对同类 setRequestedFrameRate frameRate=NaN 日志刷屏的修复。建议先检查项目实际使用的 Compose UI 版本,确认是否包含该修复。如果暂时无法升级,且功能没有异常,可以过滤这条日志,方便继续调试。
frameRate=NaN 表示什么?
这里的 frameRate 是 View 的请求帧率参数,与动画中的 alpha 是两个不同的值。Android 的 View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT 常量本身就使用 NaN,因此看到它不能直接推断透明度计算异常,也不能把它当作实际测量到的屏幕帧率。参见 View API 文档。
Compose 官方的一项修复与这个现象直接相关。在 API 35 及以上版本中,部分 Compose 实现会在 AndroidComposeView.dispatchDraw 中提交帧率请求。由于内部状态重置以及 NaN 比较的处理方式,绘制时可能反复提交相同的默认请求,产生大量日志。该修复记录上次提交的值,并对两次值均为 NaN 的情况单独处理,避免重复提交。参见 Compose 官方修复提交。
这是一个明确的排查方向,但原问题没有提供 Compose 依赖版本和完整日志,无法确认每台设备上的日志是否来自同一个缺陷,也不能认定它只影响 Samsung。
优先检查 Compose UI 依赖
Android Studio 和手机系统已经更新,并不代表应用打包使用的 Compose UI 依赖也已经更新。
如果应用模块名为 app,并使用常见的 debugRuntimeClasspath 配置,可以运行:
./gradlew :app:dependencyInsight --dependency androidx.compose.ui:ui --configuration debugRuntimeClasspath
如果使用了其他模块名、构建变体或 product flavor,需要替换对应名称。重点查看 Gradle 最终解析出的版本,确认是否因依赖约束或传递依赖发生版本覆盖。
接下来确认所用的 Compose UI 发布版本是否包含上述修复,再按项目的兼容要求升级。使用 Compose BOM 的项目也要检查实际解析结果。现有依据只能确认源码中存在修复,不足以指定一个已核实的最低修复发布版本。
升级后,在同一台设备上复测同一段动画,比较日志频率,并检查画面是否正常。
用静态透明度做对照
可以暂时移除题述中的 rememberInfiniteTransition() 和 animateFloat() 声明,替换为:
val alpha = 1f
保留界面中原来使用 alpha 的位置,观察日志是否停止或明显减少。
如果日志随动画停止而减少,只能说明它与持续绘制有关,还不能据此认定 tween、infiniteRepeatable 或 RepeatMode.Restart 的用法有误。如果日志仍持续出现,应检查页面中的其他动画、滚动或持续刷新的内容。
原代码表达的是透明度在 4 秒内从 0f 变化到 1f,随后重新开始。没有依据要求为了这条日志修改动画时长或重复模式。
只影响日志阅读时,如何临时过滤?
在 Android Studio 的 Logcat 查询栏中输入:
package:mine -message:"setRequestedFrameRate frameRate=NaN"
这会显示当前项目的日志,并排除包含指定文本的消息。message 字段支持使用前缀 - 排除匹配内容,具体语法见 Logcat 官方文档。
过滤只改变日志显示,不会阻止设备生成日志,也不会修复重复帧率请求。排查崩溃、卡顿或黑屏时,应保留一份未过滤的日志,以免遗漏相关线索。
如果同时存在卡顿或崩溃
应分别收集异常堆栈和帧耗时证据,不能仅凭这两行日志判断性能问题。记录设备型号、系统构建号、实际 Compose UI 版本,以及关闭动画前后的差异。如果升级到确认包含修复的版本后仍能复现,再提交最小复现工程和完整日志。
备注:内容仅供参考。
