tencent-tds/kuiklyui-ai

kuikly-recomposition-analyzer

Analyze KuiklyUI Compose DSL recomposition performance issues from Recomposition Profiler output.

First seen May 14, 2026

Installation

$ npx skills add tencent-tds/kuiklyui-ai --skill kuikly-recomposition-analyzer

Summary

  • Analyze KuiklyUI Compose DSL recomposition performance issues from Recomposition Profiler output.
  • Use when the user mentions 重组分析、重组优化、卡顿分析、recomp 报告、recomposition analysis, or asks to analyze profiler_report.json / profiler_frames.jsonl log files generated by KuiklyUI Recomposition Profiler.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from tencent-tds/kuiklyui-ai · top by installs.

npx skills add tencent-tds/kuiklyui-ai

Browse all from tencent-tds/kuiklyui-ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 122
License LICENSE
Default branch main
Open issues 1
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 15,479 B
  • docs SUMMARY.md 366 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 285 installs

SKILL.md

Kuikly Recomposition Analyzer

三阶段漏斗分析 KuiklyUI Compose DSL 的重组性能问题:report 筛查 → frames 深挖 → 源码确认。

阈值配置

读取 references/config.md 获取默认阈值。用户在请求中指定参数可覆盖(如 scopeCountThreshold=10)。

工作流

Phase 0 — 获取日志

读取 references/log-format.md 了解日志字段格式。按优先级获取 profilerreport.jsonprofilerframes.jsonl

  1. 用户直接提供路径 → Read 工具读取
  2. 检查当前目录约定路径:./profilerlogs/./profilerreport.json
  3. 自动从设备拉取 → 读 references/log-retrieval.md 执行对应平台命令
  4. 均失败 → 输出引导:

> 未找到 profiler 日志。请先采集数据: > 1. 在代码中调用 RecompositionProfiler.start() 开始录制,操作完成后调用 stop() > 2. 或在 Profiler Overlay 面板点击「开始」录制,操作完成后点击「停止」,再点击「获取报告」 > 3. 采集完成后告诉我文件路径,或提供 App 包名让我来拉取

Phase 1 — 数据健康检查

  • totalFrames < minFramesThreshold(默认 30)→ 告警,询问是否继续
  • totalRecompositions == 0 → 提示无重组记录
  • filteredNames 非空 → 报告中声明排除的组件

Phase 2 — Report 筛查

前置步骤(必须先执行):按 recompositionCount 降序排列所有非 noScope 组件,列出 TOP 20。任何 recompositionCount > 50 的组件必须进入报告,无论总耗时多低。这一步防止高频但低耗时的组件被后续按总耗时排序时遗漏。

references/detection-rules.md,遍历 composables[]

条件 处理
noScopeRecompositions == recompositionCount 归入正常重组清单,跳过后续分析
maxDurationMs > singleRecompDurationThreshold(默认 10ms) 无论重组次数多少,必须输出到报告,进入 Phase 3 深挖
scopeDistribution 某 key 计数 > scopeCountThreshold 标记嫌疑,进入 Phase 3
paramChangeFrequency["#N"] / recompositionCount > paramChangeRateThreshold 标记 RULE-C 嫌疑(需结合源码判断参数类型)
triggerStates[i].readers.length > stateReadersThreshold 标记 RULE-B 嫌疑

Phase 3 — Frames 深挖 + 源码确认

注意profiler_frames.jsonl 是 JSONL 格式(每行一个独立 JSON 对象),不是单个 JSON 文件。必须逐行读取并 parse,不能整个文件当 JSON 解析。用 Read 工具读取后按行处理。

如果分析对象是 LazyList/LazyGrid/Pager 内的 item 组件,读取 references/lazylist-rules.md 了解 item 闭包重建与业务组件 skip 的区别。

逐行读 profilerframes.jsonl,按 type 字段分流(frame / touchcontext / scroll_context)。

帧级检查:

对每个耗时超标帧,按以下流程处理:

  1. 先判断是否正常

- 对照 scroll_context:若该帧紧跟滚动事件,且帧内事件以 noScope(首次组合)为主 → 归为正常渲染开销,在报告中简短说明原因,不进入后续分析 - 若帧内事件数很多但绝大多数是 noScope → 同上,属于列表滑入时的正常批量首次组合

  1. 确认是真实问题后,做帧内根因分析

- 找出帧内耗时最长的 composable 事件(durationMs 最大的几个) - 检查这些组件是否被同一个 State 级联触发(triggerStates 相同) - 对耗时最高的组件执行完整的链式推理(Step 1-5,同嫌疑项流程)

  1. 报告中每个真实问题帧必须包含

- 帧耗时 + 帧内事件数 - 判断结论(正常 / 有问题)及理由 - 若有问题:耗时最高的 1-3 个组件的名称、耗时、触发 State - 根因分析(参照链式推理 Step 2-4) - 优化建议(有具体方向时给出,无法判断时说明需要补充什么信息)

  • 单次 composable_recomposed.durationMs > durationThreshold → 进入链式推理
  • 同帧多组件被同一 State 触发 → 级联嫌疑,分析该 State 的写入时机

上下文辅助判断(touch/scroll 可用时):

  • touchBegin~touchEnd 之间某 scope 重组 > 3 次 → 标注「一次点击触发 N 次重组,疑似可优化」
  • scroll_context index 变化 + item 重组 ≈ 滑入数量 → 归入正常
  • scroll_context index 未变 + item 重组 → 标注「非滚动导致的重组,需分析」

源码确认(仅对确认嫌疑项):

对每个嫌疑项,按以下链式推理步骤深入分析(不得跳过):

Step 1 — 定位代码sourceLocation(格式 FileName.kt:行号),用 Glob "**/<FileName>.kt" 定位文件,读取函数声明及其周围 30 行代码。

Step 2 — 理解数据信号 回答:这个组件的 scopeDistribution 显示哪个 scope 被反复触发?triggerStates 显示是哪个 State 在驱动?paramChangeFrequency 中哪个参数每次都在变?把具体数值写出来(如「scope=223833166 被触发 61 次,平均耗时 0.75ms」)。

如果 paramChangeFrequency 显示某参数高频变化,必须先判断变化的本质

  • 业务数据确实在变(如滚动时坐标每帧不同、翻页时列表内容更新)→ 根因是写入逻辑,不是类型稳定性问题
  • 数据内容没变但引用变了(每次传入新实例,值相同但 === 不等)→ 才是类型稳定性或对象创建问题

两者根因完全不同,不能混淆。

Step 3 — 追溯根因 结合代码,回答:这个 State 是谁写入的?在什么时机写入?为什么每次重组都会触发?找到真正的"写入者"(不是"读取者")。如果 State 是在 LaunchedEffect / onGloballyPositioned / snapshotFlow 等副作用中写入,说明具体的触发时机。

对于 CompositionLocal 子树重组,额外回答:传入 CompositionLocalProvider 的值是新实例还是缓存实例?CompositionLocalProvider=== 引用比较,即使内容相同,每次传入新实例都会触发整个子树重组。根因可能是「每次重组都 copy()/新建对象」,不一定是类型不稳定。

判断参数变化根因的通用流程(适用于任何 paramChangeFrequency 高频情况):

  1. 读源码找到参数的调用侧——是谁在传这个参数?
  2. 传入的是新建对象(copy()listOf()、lambda)还是稳定引用(单例、remember 缓存)?
  3. 如果是新建对象:检查是否有必要每次新建,还是可以用 remember 缓存
  4. 如果是稳定引用但还是判定为变化:才考虑类型稳定性(是否有 varList、跨模块类型)

RULE-C 专项:命中 RULE-C 时额外执行

读取 references/stability-rules.md 了解完整的稳定性判断规则,然后:

  • 读源码,按声明顺序将 #N 对应到具体参数名和类型
  • 先判断变化本质:参数值每次确实不同(业务数据在变)?还是值相同但每次传入新实例(引用不等)?前者不是稳定性问题,后者才考虑类型稳定性
  • @Stable/@Immutable 注解会覆盖编译器推断:加了注解的类,编译器信任其稳定,不会因 var/List 判为不稳定。若加了 @Stable 但参数仍 100% 变化,真正原因是「每次传入新实例」而非类型推断问题
  • 注意 @Stable + var 直接赋值的 bug:skip 会发生,但界面不更新(显示过时数据),比「不 skip」更危险
  • 若确认是「相同值重复创建新实例」,再按类型选方案,详见 references/optimization-patterns.md
  • Strong Skipping 已开启时,禁止建议手写 remember { { ... } } 包裹 lambda——手写是多余的。若 lambda 参数仍高频变化,问题在 lambda 捕获的变量稳定性

Step 4 — 评估影响范围 回答:这个 State 被几个组件订阅(readers)?这些组件是否都真的需要在每次 State 变化时重组?哪些是可以 skip 的?哪些是必须响应的?

Step 5 — 提出方案并说明权衡 读取 references/optimization-patterns.md 获取对应规则的优化方案。给出 1-2 个具体优化方案,每个方案必须:

  • 提供修改前/后的代码对比
  • 说明为什么这个改法能解决问题(从 Compose 运行时机制角度解释)
  • 说明可能的副作用或注意事项
  • 如果有多个方案,说明推荐哪个,以及在什么场景下选另一个

推荐加 @Stable/@Immutable 注解前,必须通过以下两项检查,任一不满足则不推荐:

  1. 类的属性是否满足注解的承诺(@Immutable = 构造后永不变;@Stable = 变化只通过 MutableState 通知)?若含 var 直接赋值,skip 仍会发生(注解让编译器信任),但界面不会更新(Compose 不知道值变了),会产生界面 bug
  2. 调用方是否会复用实例或传相同引用?若每次都 copy()/new/listOf() 创建新对象传入,注解无法让 skip 发生

如果两项检查通不过,不要推荐加注解,而是从调用方如何传参数据模型如何设计角度给出方案。

如果遇到分析受限的情况(无法定位源码、参数索引无法映射等),读取 references/known-limitations.md 确认是否属于已知限制,按限制说明处理。

Phase 4 — 输出报告

references/report-template.md,生成:

  1. 对话摘要:数据概览 + TOP 3 问题
  2. Markdown 报告recomp-analysis-YYYYMMDD-HHmm.md,含数据概览、正常重组清单、问题诊断(按严重度降序)、过滤配置声明。严重度评级和排序规则见 references/detection-rules.md总耗时(重组次数 × 平均单次耗时)为第一排序维度,次数多但单次耗时极低的问题排在真正耗时高的问题之后。

报告写作规范(必须遵守):

  1. 数据概览的帧统计:只写帧数,不写占比。例如「慢帧:14 帧」,不写「14 帧(占 7.4%)」。
  2. 问题描述禁止使用内部术语:不得在问题描述中出现 RULE-ARULE-BRULE-CRULE-SCOPE 等字眼。用用户能理解的语言描述,例如「每次滚动都触发该组件重渲染」而不是「命中 RULE-B」。规则标识只允许出现在过滤配置声明段。
  3. 上下文描述要区分触发来源:描述重组次数时必须明确是「一次点击触发 N 次重组」还是「N 帧滚动累计触发 M 次重组」,两者不可混用。若是跨多帧的累计,说明「在 X 帧滚动过程中,该组件共重组 N 次」。
  4. 每个问题必须包含两个关键数据:① 同一 scope 触发的重组次数(或总重组次数);② 平均单次耗时(avgDurationMs)。缺少任一数据时标注「数据不足,无法评估严重程度」。
  5. 问题分析必须有深度:根因分析要说明「谁在写这个 State、在什么时机写、为什么频繁触发」,不能只说「State 变化导致重组」。优化建议必须提供修改前后的代码对比,并解释为什么这个改法有效,不能只给出结论。
  6. 原因不明时直接告知,并给出排查引导:如果某个问题(如单次耗时异常)通过现有日志和源码无法定位根因,不要猜测或给出模糊结论。直接写:「当前日志不足以确定根因,建议进一步排查」,并给出具体的排查建议,例如:

- 在该组件函数体内增加耗时打点(measureTimeMillis)定位慢在哪个子操作 - 或直接说「可以告诉我,我来帮你做更深入的分析」

  1. 重组次数高但总耗时低的组件不能省略:所有命中检测规则的组件都必须出现在报告中,不能因为总耗时低就跳过。对于重组次数明显偏高(如 >50 次)但单次耗时极低(<0.5ms)的组件:

- 仍然列入报告,标注严重度为「低」 - 说明重组次数和平均耗时 - 如果未做深入分析,明确注明「单次耗时极低,暂未深入分析,但重组次数偏高,建议关注」 - 对于次数极高的(如 >100 次),即使耗时低也应做简要根因分析(至少说明是什么 State 在驱动、是否可以减少重组次数)

  1. 尊重已有的 @Stable/@Immutable 注解,不质疑其准确性

- 类已标注 @Stable@Immutable → 编译器信任它是稳定的,不要说「标注不准确」「标注是无效的」 - 类含 Map/List/lambda 属性但已标注 @Immutable → 注解覆盖了编译器推断,这是开发者的有意设计,不是错误 - Strong Skipping 已开启时,@Stable 类含 lambda 属性 → lambda 自动 remember,引用稳定,不要说「lambda 引用稳定性取决于调用方」 - 如果加了注解的类参数仍然高频变化,问题在调用方每次传入新实例,不是注解有问题。分析方向是调用方如何传参,而不是质疑注解

  1. 建议使用 remember 缓存对象时,必须分析依赖项

- 禁止直接建议无 key 的 remember {},除非已确认对象创建不依赖任何外部状态 - 如果工厂函数内部可能读取 CompositionLocal(如主题色、字体大小、深色模式)→ remember 必须带正确的 key(如 remember(isDarkTheme) { markdownColor() }),否则主题切换后配置不更新,造成界面 bug - 如果无法确认工厂函数的内部依赖(没有读到源码)→ 不建议 remember,而是建议「检查该函数是否依赖主题等外部状态后再决定是否缓存」 - 错误示例:val colors = remember { markdownColor() } — 如果 markdownColor() 读了深色模式,切换主题后颜色不更新 - 正确示例:val isDark = isAppInDarkTheme(); val colors = remember(isDark) { markdownColor() }

形态 C:聚焦特定页面

用户说「我只想看 XX 页」时:

请在 profiler 面板点「重置」按钮,进入目标页面操作一遍,然后让我分析。

References

  • references/config.md — 可配置阈值默认值
  • references/log-format.md — report.json / frames.jsonl 字段说明
  • references/log-retrieval.md — 各平台(adb/xcrun/hdc)拉取命令
  • references/detection-rules.md — 检测规则详细逻辑
  • references/lazylist-rules.md — LazyList item 重组分析规则(闭包重建 vs 业务组件 skip、错误结论规避)
  • references/stability-rules.md — Compose 稳定性规则(已实测验证):编译器推断规则、skip 条件、注解有效/危险场景、Profiler 中的表现差异
  • references/optimization-patterns.md — 每条规则对应的优化方案和代码样例
  • references/known-limitations.md — 已知限制(paramChanges 索引无参数名、不稳定类型 scope 重建等)
  • references/report-template.md — Markdown 报告模板