Claude Code 实践(2) - 3分14秒实现必现性能问题从分析到修复建议

记录时间:2026-01-28 | 项目:MiSettings(小米系统设置) | 协作工具:Claude Code

背景

小米系统设置里有个”屏幕时间管理”功能,打开月维度的使用统计页面时,经常要等几秒钟才能看到数据。这是一个确定性的性能问题,不是偶发,每次打开月视图都能重现。

我对这个代码库不熟悉,手上有一份刚抓的 bugreport(300 万行),想让 Claude Code 帮我定位根因。

分析过程

整个过程分三步,总共耗时3分14秒。

第一步:建立代码地图

先让 Claude Code 扫描整个代码库中”屏幕时间管理”相关的代码。它识别出三个核心模块:

模块语言职责
screentimeKotlin统计 + 本地限制(新架构)
greenguardJava远程家长控制
module_parental_controlJava家长控制 UI

这一步的价值是快速理解一个陌生代码库的模块边界,不用自己翻目录。

第二步:追踪数据加载链路

确认性能问题在 screentime 模块后,Claude Code 沿着数据加载路径逐层读取代码:

 
ScreenTimeDetailPage(Activity)​
 ScreenTimeDetailViewModel.loadData()
 getPageDetail()  GetDeviceAppUsage​
 DeviceAppUsageRepository.getDeviceAppUsagesInPeriods()
 按天循环:getDeviceAppUsagePerDay()
 UsageStatsWrapperRepository.getUsageStatsForUserPerDay()
 UsageStatsRepository.getUsageStatsForUserPerDay()
 按小时循环:UsageStatsDatasource.calculateUsageStats()
 Android UsageStats API​

读完这条链路上的 6 个文件,性能瓶颈就清楚了。

第三步:量化瓶颈

月视图(rangeIndex=0)的数据范围是最近 30 天,加上用于”对比上期”的前 30 天,共 60 天。数据加载过程中有三层放大:

  1. 60 天串行循环:for (day in startBeginTime until endBeginTime step INTERVAL_DAY) 逐天处理,天与天之间没有并行

  2. 每天 24 小时遍历:for (hourIndex in 0 until COUNT_HOURS_OF_DAY) 逐小时查询系统 API 或缓存

  3. 双用户空间:主空间和手机分身各查一次(这层已经做了并行)

🎁

最坏情况(首次打开,无缓存):60 × 24 × 2 = 2880 次系统 API 调用。常规情况(历史天有 DB 缓存):59 次 DB 查询 + 48 次 API 调用,估算 1.7-2.9 秒

bugreport 也印证了这个判断:Activity 在 23:57:07 启动,167ms 完成首帧绘制,但用户从 23:57:09 到 23:57:27 之间反复点击了 10 多次——页面已经渲染,但数据区域还在 loading。

根因总结

性能问题的本质是查询扇出过大且串行执行。月视图需要 60 天数据,每天独立查询 DB 或系统 API,天与天之间串行。当期和上期也串行。缓存策略只在小时级和天级生效,没有月级聚合缓存。

优化方案

按优先级排列,附量化预期:

#方案做法效果
1当期和上期并行加载两个 30 天查询从串行改为 coroutineScope { async + async }总耗时从 T1+T2 降到 max(T1,T2),节省约 40-50%
2天级查询并行化for 循环改为 map { async }.awaitAll(),受限 CPU 核心数约 8 路并行DB 命中场景从 ~200ms 降到 ~30ms
3批量 DB 查询新增 getAppUsageByDateRange 接口,一次查出整月数据118 次 DB 查询 → 2 次,节省 ~350-550ms
4上期数据延迟加载先展示当期数据,上期异步加载后刷新对比文案用户可见的首屏时间直接减半

综合效果

常规场景从 1.7-2.9s 降到 100-400ms,首次无缓存场景从 5-10s 降到 0.5-1s。

附带发现:rangeIndex 映射 bug

通读代码时还发现 AbsDetailViewModel 中 Month 和 Week 的 rangeIndex 变量写反了——getter 和 setter 对称地反,所以单 tab 使用时自洽,只有跨 tab 切换翻页后才触发错误的数据范围。这个 bug 还间接影响 MemoryCache 命中率(key 里包含 rangeIndex)。属于典型的复制粘贴错误,人工 review 很难注意到,AI 逐行阅读时直接捕获。

方法论

这次实践的核心模式是 AI 做全链路代码通读

传统的性能排查思路是”先量后猜”:看 trace、看火焰图、定位热点函数。但面对一个不熟悉的代码库,连 profiling 工具都还没接上,光是理解代码结构就要花不少时间。

Claude Code 的做法不同:直接从 UI 入口开始,沿调用链逐文件读取,读到数据源为止。它不需要运行时数据,纯靠代码静态分析就能识别出”60 天串行循环 × 24 小时遍历”这种结构性瓶颈。这类问题用 profiler 当然也能发现,但 AI 的优势是速度——从打开代码库到产出完整分析(含优化方案和量化预期),整个对话不到 10 轮。