Compose 列表页落地复盘:把真机滚动回归纳入 baseline没补齐时问题会怎么连锁放大
## Android界面设计里这类问题怎么出现
如果滚动锚点和 item key 都不稳定,列表偶发闪动其实很难靠截图复盘,必须先收集状态切换证据。这次不再沿着旧稿的铺陈方式展开,而是把 列表状态、滚动锚点和重组预算 里的失效信号、止血动作和回归证据拆成三段,保证 Compose 列表页 的观察路径和上一轮不同。如果上一轮已经写过常规背景,这一版就直接从 给关键 item 建立重组预算、把真机滚动回归纳入 baseline 和现场证据切进去,让 Compose 列表页 的正文骨架明显换轨。
## Compose 列表页解决方案
方案上不要急着推翻整条链路,先把最危险的节点单点替换掉,再用 Compose Compiler metrics 和 JankStats 把新旧行为对齐。只要状态生产者、状态消费者、兜底重试这三层没有明确 owner,Compose 列表页 后面一定还会反复炸,所以这里先把责任边界钉住。先把列表 state、筛选 state、effect 触发拆分清楚,再用稳定 key 和最小重组范围去收口。
## 直接可抄片段
这一版示例故意换成另一套骨架:围绕 列表状态、滚动锚点和重组预算 先给结构或审计对象,再给命令或校验入口,最后再贴核心实现。下面这组片段按 Compose 列表页 的真实处理顺序展开:先贴核心实现,再给排查命令,最后补一段修复辅助代码。
1. 排查命令
./gradlew :app:assembleRelease -PcomposeCompilerReports=true
adb shell dumpsys gfxinfo com.example.app framestats
2. 核心实现
@Stable
data class TimelineItemUi(
val id: String,
val title: String,
val subtitle: String
)
fun TimelineItemUi.renderKey(): String = id
3. 修复辅助代码
data class JiemianshejiRecompositionBudgetCheckResult(
val key: String,
val ok: Boolean,
val detail: String
)
## Compose 列表页注意点
Compose Compiler metrics、JankStats、Baseline Profile 这类现成观测手段不要浪费,很多问题不是没有证据,而是证据没有被串成同一条时间线。真正要避开的不是标题撞车,而是 Compose 列表页 还沿用同一套观察路径和收尾话术,所以这一版专门把坑位改写到 列表状态、滚动锚点和重组预算 相关的边界。如果同分类最近文章已经覆盖过常规背景,这次就直接补旧稿没展开的失败信号、止血顺序和验收证据。
## Compose 列表页常见异常
1. 任务重复执行
如果修复后还是偶发重入,说明幂等、入口锁或回退动作还没真正闭环,继续补最短验证路径。
@Stable
data class TimelineItemUi(
val id: String,
val title: String,
val subtitle: String
)
fun TimelineItemUi.renderKey(): String = id
2. 状态不一致
如果现场出现状态和预期不一致,先用 Compose Compiler metrics 拉证据,再按 给关键 item 建立重组预算、把图片和列表状态解耦 的顺序收口。
./gradlew :app:assembleRelease -PcomposeCompilerReports=true
adb shell dumpsys gfxinfo com.example.app framestats
## Compose 列表页最小可运行示例
这个最小样例的职责不是重复上轮步骤,而是把 列表状态、滚动锚点和重组预算 对应的新验证路径单独跑通,确保第二轮文章和上一轮不是同构改写。最后留一个最小可运行片段,重点不是完整业务,而是让 Compose 列表页 能在本地快速复现、快速验证。这段最适合直接扔进 demo、测试工程或排障脚本库里,后面团队再回头看 Android界面设计 的问题时能直接复用。
1. 本地验证命令
./gradlew :app:assembleRelease -PcomposeCompilerReports=true
adb shell dumpsys gfxinfo com.example.app framestats
2. 最小数据结构
data class JiemianshejiRecompositionBudgetState(
val id: String,
val status: String
)
3. 最小执行入口
fun main() {
println(JiemianshejiRecompositionBudgetState("1", "ok"))
}
