kuikly-recomposition-analyzer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKuikly Recomposition Analyzer
Kuikly Recomposition Analyzer
三阶段漏斗分析 KuiklyUI Compose DSL 的重组性能问题:report 筛查 → frames 深挖 → 源码确认。
Three-stage funnel analysis for KuiklyUI Compose DSL recomposition performance issues: Report Screening → Frames Deep Dive → Source Code Verification.
阈值配置
Threshold Configuration
读取 获取默认阈值。用户在请求中指定参数可覆盖(如 )。
references/config.mdscopeCountThreshold=10Read to get default thresholds. Users can override them by specifying parameters in requests (e.g., ).
references/config.mdscopeCountThreshold=10工作流
Workflow
Phase 0 — 获取日志
Phase 0 — Obtain Logs
读取 了解日志字段格式。按优先级获取 和 :
references/log-format.mdprofiler_report.jsonprofiler_frames.jsonl- 用户直接提供路径 → Read 工具读取
- 检查当前目录约定路径:、
./profiler_logs/./profiler_report.json - 自动从设备拉取 → 读 执行对应平台命令
references/log-retrieval.md - 均失败 → 输出引导:
未找到 profiler 日志。请先采集数据:
- 在代码中调用 开始录制,操作完成后调用
RecompositionProfiler.start()stop() - 或在 Profiler Overlay 面板点击「开始」录制,操作完成后点击「停止」,再点击「获取报告」
- 采集完成后告诉我文件路径,或提供 App 包名让我来拉取
- 在代码中调用
Read to understand log field formats. Retrieve and in the following priority order:
references/log-format.mdprofiler_report.jsonprofiler_frames.jsonl- User provides direct path → Read via Read tool
- Check conventional paths in current directory: ,
./profiler_logs/./profiler_report.json - Automatically pull from device → Execute platform-specific commands as described in
references/log-retrieval.md - If all fail → Output guidance:
Profiler logs not found. Please collect data first:
- Call in code to start recording, then call
RecompositionProfiler.start()after operations are completedstop() - Or click "Start" in the Profiler Overlay panel to record, click "Stop" after operations, then click "Get Report"
- After collection, tell me the file path, or provide the App package name and I'll pull it for you
- Call
Phase 1 — 数据健康检查
Phase 1 — Data Health Check
- (默认 30)→ 告警,询问是否继续
totalFrames < minFramesThreshold - → 提示无重组记录
totalRecompositions == 0 - 非空 → 报告中声明排除的组件
filteredNames
- (default 30) → Issue warning and ask whether to continue
totalFrames < minFramesThreshold - → Prompt that no recomposition records exist
totalRecompositions == 0 - is not empty → Declare excluded components in the report
filteredNames
Phase 2 — Report 筛查
Phase 2 — Report Screening
前置步骤(必须先执行):按 降序排列所有非 noScope 组件,列出 TOP 20。任何 的组件必须进入报告,无论总耗时多低。这一步防止高频但低耗时的组件被后续按总耗时排序时遗漏。
recompositionCountrecompositionCount > 50读 ,遍历 :
references/detection-rules.mdcomposables[]| 条件 | 处理 |
|---|---|
| 归入正常重组清单,跳过后续分析 |
| 无论重组次数多少,必须输出到报告,进入 Phase 3 深挖 |
| 标记嫌疑,进入 Phase 3 |
| 标记 RULE-C 嫌疑(需结合源码判断参数类型) |
| 标记 RULE-B 嫌疑 |
Prerequisite (must execute first): Sort all non-noScope components in descending order by , list TOP 20. Any component with must be included in the report, regardless of total duration. This step prevents high-frequency but low-duration components from being missed when sorted by total duration later.
recompositionCountrecompositionCount > 50Read and iterate through :
references/detection-rules.mdcomposables[]| Condition | Handling |
|---|---|
| Classify into Normal Recomposition List, skip subsequent analysis |
| Must be output to report regardless of recomposition count, proceed to Phase 3 for deep dive |
Count of a key in | Mark as suspicious, proceed to Phase 3 |
| Mark as RULE-C suspicion (need to combine source code to judge parameter type) |
| Mark as RULE-B suspicion |
Phase 3 — Frames 深挖 + 源码确认
Phase 3 — Frames Deep Dive + Source Code Verification
注意: 是 JSONL 格式(每行一个独立 JSON 对象),不是单个 JSON 文件。必须逐行读取并 parse,不能整个文件当 JSON 解析。用 Read 工具读取后按行处理。
profiler_frames.jsonl如果分析对象是 LazyList/LazyGrid/Pager 内的 item 组件,读取 了解 item 闭包重建与业务组件 skip 的区别。
references/lazylist-rules.md逐行读 ,按 字段分流( / / )。
profiler_frames.jsonltypeframetouch_contextscroll_context帧级检查:
对每个耗时超标帧,按以下流程处理:
-
先判断是否正常:
- 对照 :若该帧紧跟滚动事件,且帧内事件以
scroll_context(首次组合)为主 → 归为正常渲染开销,在报告中简短说明原因,不进入后续分析noScope - 若帧内事件数很多但绝大多数是 noScope → 同上,属于列表滑入时的正常批量首次组合
- 对照
-
确认是真实问题后,做帧内根因分析:
- 找出帧内耗时最长的 composable 事件(最大的几个)
durationMs - 检查这些组件是否被同一个 State 级联触发(相同)
triggerStates - 对耗时最高的组件执行完整的链式推理(Step 1-5,同嫌疑项流程)
- 找出帧内耗时最长的 composable 事件(
-
报告中每个真实问题帧必须包含:
- 帧耗时 + 帧内事件数
- 判断结论(正常 / 有问题)及理由
- 若有问题:耗时最高的 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 — 定位代码
取 (格式 ),用 定位文件,读取函数声明及其周围 30 行代码。
sourceLocationFileName.kt:行号Glob "**/<FileName>.kt"Step 2 — 理解数据信号
回答:这个组件的 显示哪个 scope 被反复触发? 显示是哪个 State 在驱动? 中哪个参数每次都在变?把具体数值写出来(如「scope=223833166 被触发 61 次,平均耗时 0.75ms」)。
scopeDistributiontriggerStatesparamChangeFrequency如果 显示某参数高频变化,必须先判断变化的本质:
paramChangeFrequency- 业务数据确实在变(如滚动时坐标每帧不同、翻页时列表内容更新)→ 根因是写入逻辑,不是类型稳定性问题
- 数据内容没变但引用变了(每次传入新实例,值相同但 不等)→ 才是类型稳定性或对象创建问题
===
两者根因完全不同,不能混淆。
Step 3 — 追溯根因
结合代码,回答:这个 State 是谁写入的?在什么时机写入?为什么每次重组都会触发?找到真正的"写入者"(不是"读取者")。如果 State 是在 / / 等副作用中写入,说明具体的触发时机。
LaunchedEffectonGloballyPositionedsnapshotFlow对于 CompositionLocal 子树重组,额外回答:传入 的值是新实例还是缓存实例? 用 引用比较,即使内容相同,每次传入新实例都会触发整个子树重组。根因可能是「每次重组都 /新建对象」,不一定是类型不稳定。
CompositionLocalProviderCompositionLocalProvider===copy()判断参数变化根因的通用流程(适用于任何 paramChangeFrequency 高频情况):
- 读源码找到参数的调用侧——是谁在传这个参数?
- 传入的是新建对象(、
copy()、lambda)还是稳定引用(单例、listOf()缓存)?remember - 如果是新建对象:检查是否有必要每次新建,还是可以用 缓存
remember - 如果是稳定引用但还是判定为变化:才考虑类型稳定性(是否有 、
var、跨模块类型)List
RULE-C 专项:命中 RULE-C 时额外执行
读取 了解完整的稳定性判断规则,然后:
references/stability-rules.md- 读源码,按声明顺序将 对应到具体参数名和类型
#N - 先判断变化本质:参数值每次确实不同(业务数据在变)?还是值相同但每次传入新实例(引用不等)?前者不是稳定性问题,后者才考虑类型稳定性
- /
@Stable注解会覆盖编译器推断:加了注解的类,编译器信任其稳定,不会因@Immutable/var判为不稳定。若加了List但参数仍 100% 变化,真正原因是「每次传入新实例」而非类型推断问题@Stable - 注意 + var 直接赋值的 bug:skip 会发生,但界面不更新(显示过时数据),比「不 skip」更危险
@Stable - 若确认是「相同值重复创建新实例」,再按类型选方案,详见
references/optimization-patterns.md - Strong Skipping 已开启时,禁止建议手写 包裹 lambda——手写是多余的。若 lambda 参数仍高频变化,问题在 lambda 捕获的变量稳定性
remember { { ... } }
Step 4 — 评估影响范围
回答:这个 State 被几个组件订阅(readers)?这些组件是否都真的需要在每次 State 变化时重组?哪些是可以 skip 的?哪些是必须响应的?
Step 5 — 提出方案并说明权衡
读取 获取对应规则的优化方案。给出 1-2 个具体优化方案,每个方案必须:
references/optimization-patterns.md- 提供修改前/后的代码对比
- 说明为什么这个改法能解决问题(从 Compose 运行时机制角度解释)
- 说明可能的副作用或注意事项
- 如果有多个方案,说明推荐哪个,以及在什么场景下选另一个
推荐加 / 注解前,必须通过以下两项检查,任一不满足则不推荐:
@Stable@Immutable- 类的属性是否满足注解的承诺(= 构造后永不变;
@Immutable= 变化只通过 MutableState 通知)?若含@Stable直接赋值,skip 仍会发生(注解让编译器信任),但界面不会更新(Compose 不知道值变了),会产生界面 bugvar - 调用方是否会复用实例或传相同引用?若每次都 /
copy()/new创建新对象传入,注解无法让 skip 发生listOf()
如果两项检查通不过,不要推荐加注解,而是从调用方如何传参或数据模型如何设计角度给出方案。
如果遇到分析受限的情况(无法定位源码、参数索引无法映射等),读取 确认是否属于已知限制,按限制说明处理。
references/known-limitations.mdNote: is in JSONL format (each line is an independent JSON object), not a single JSON file. Must read and parse line by line, not parse the entire file as JSON. Use the Read tool to read and process line by line.
profiler_frames.jsonlIf the analysis target is an item component inside LazyList/LazyGrid/Pager, read to understand the difference between item closure reconstruction and business component skip.
references/lazylist-rules.mdRead line by line, and route by the field ( / / ).
profiler_frames.jsonltypeframetouch_contextscroll_contextFrame-level Check:
For each frame with excessive duration, process as follows:
-
First judge if it's normal:
- Cross-reference with : If the frame follows a scroll event and most events in the frame are
scroll_context(first-time composition) → Classify as normal rendering overhead, briefly explain the reason in the report, and skip subsequent analysisnoScope - If there are many events in the frame but most are noScope → Same as above, belongs to normal batch first-time composition when list slides in
- Cross-reference with
-
After confirming it's a real issue, perform root cause analysis within the frame:
- Find the composable events with the longest duration in the frame (top few with largest )
durationMs - Check if these components are triggered cascadingly by the same State (same )
triggerStates - Perform complete chain reasoning (Step 1-5, same as suspicious item process) for the component with the highest duration
- Find the composable events with the longest duration in the frame (top few with largest
-
Each real issue frame in the report must include:
- Frame duration + number of events in the frame
- Judgment conclusion (normal / problematic) and reasons
- If problematic: Names, durations, and triggering States of the top 1-3 components with highest duration
- Root cause analysis (refer to chain reasoning Step 2-4)
- Optimization suggestions (give specific directions if possible, explain what information needs to be supplemented if unable to judge)
- Single → Enter chain reasoning
composable_recomposed.durationMs > durationThreshold - Multiple components in the same frame are triggered by the same State → Cascading suspicion, analyze the writing timing of this State
Context-assisted Judgment (when touch/scroll is available):
- A scope recomposes >3 times between touchBegin~touchEnd → Mark as "One click triggers N recompositions, suspected to be optimizable"
- scroll_context index changes + item recompositions ≈ number of slid-in items → Classify as normal
- scroll_context index unchanged + item recompositions → Mark as "Recomposition not caused by scrolling, needs analysis"
Source Code Verification (only for confirmed suspicious items):
For each suspicious item, perform in-depth analysis following the chain reasoning steps (cannot skip):
Step 1 — Locate Code
Take (format ), use to locate the file, and read the function declaration and 30 lines of surrounding code.
sourceLocationFileName.kt:line numberGlob "**/<FileName>.kt"Step 2 — Understand Data Signals
Answer: Which scope is repeatedly triggered according to the component's ? Which State is driving it according to ? Which parameter changes every time in ? Write out the specific values (e.g., "scope=223833166 was triggered 61 times, average duration 0.75ms").
scopeDistributiontriggerStatesparamChangeFrequencyIf shows a parameter changes frequently, must first judge the nature of the change:
paramChangeFrequency- Business data actually changes (e.g., coordinates differ per frame during scrolling, list content updates during page turning) → Root cause is writing logic, not type stability issue
- Data content remains the same but reference changes (new instance passed each time, values are the same but is not equal) → This is a type stability or object creation issue
===
The root causes of the two are completely different and cannot be confused.
Step 3 — Trace Root Cause
Combine with code, answer: Who is writing this State? When is it written? Why is it triggered every recomposition? Find the real "writer" (not "reader"). If the State is written in side effects like / / , specify the triggering timing.
LaunchedEffectonGloballyPositionedsnapshotFlowFor CompositionLocal subtree recomposition, additionally answer: Is the value passed to a new instance or a cached instance? uses reference comparison; even if the content is the same, passing a new instance each time will trigger recomposition of the entire subtree. The root cause may be "copy()/new object created every recomposition", not necessarily type instability.
CompositionLocalProviderCompositionLocalProvider===General Process for Judging Root Cause of Parameter Changes (applicable to any high-frequency paramChangeFrequency situation):
- Read source code to find the caller side of the parameter — Who is passing this parameter?
- Is a new object (,
copy(), lambda) passed, or a stable reference (singleton,listOf()cached)?remember - If it's a new object: Check if it's necessary to create a new one every time, or if it can be cached with
remember - If it's a stable reference but still judged as changed: Consider type stability (whether there is ,
var, cross-module type)List
RULE-C Special: Additional Execution When RULE-C is Hit
Read to understand complete stability judgment rules, then:
references/stability-rules.md- Read source code, map to specific parameter names and types in declaration order
#N - First judge the nature of the change: Does the parameter value actually differ each time (business data changes)? Or is the value the same but a new instance is passed each time (reference not equal)? The former is not a stability issue, the latter requires consideration of type stability
- /
@Stableannotations override compiler inference: For classes with annotations, the compiler trusts their stability and will not judge them as unstable due to@Immutable/var. If a parameter withListstill changes 100% of the time, the real reason is "new instance passed each time" rather than type inference issue@Stable - Note the bug of + var direct assignment: Skip will occur, but the interface will not update (display outdated data), which is more dangerous than "not skipping"
@Stable - If confirmed as "same value repeatedly creating new instances", select a solution according to the type, see for details
references/optimization-patterns.md - When Strong Skipping is enabled, it is forbidden to suggest wrapping lambda with handwritten — Handwriting is redundant. If lambda parameters still change frequently, the problem lies in the stability of variables captured by lambda
remember { { ... } }
Step 4 — Evaluate Impact Scope
Answer: How many components subscribe to this State (readers)? Do all these components really need to recompose every time the State changes? Which can be skipped? Which must respond?
Step 5 — Propose Solutions and Explain Trade-offs
Read to obtain optimization solutions corresponding to each rule. Provide 1-2 specific optimization solutions, each must:
references/optimization-patterns.md- Provide code comparison before/after modification
- Explain why this modification solves the problem (from the perspective of Compose runtime mechanism)
- Explain possible side effects or precautions
- If there are multiple solutions, explain which is recommended and under what scenarios to choose the other
Before recommending adding / annotations, must pass the following two checks; do not recommend if either is not met:
@Stable@Immutable- Do the properties of the class meet the promise of the annotation (= never changes after construction;
@Immutable= changes are only notified via MutableState)? If it contains@Stabledirect assignment, skip will still occur (annotation makes the compiler trust it), but the interface will not update (Compose doesn't know the value changed), which will cause interface bugsvar - Will the caller reuse instances or pass the same reference? If /
copy()/newis used to create new objects every time, annotations cannot make skip happenlistOf()
If the two checks fail, do not recommend adding annotations, but provide solutions from the perspective of how the caller passes parameters or how the data model is designed.
If encountering situations where analysis is limited (unable to locate source code, parameter index cannot be mapped, etc.), read to confirm if it belongs to known limitations, and handle according to the limitation description.
references/known-limitations.mdPhase 4 — 输出报告
Phase 4 — Output Report
读 ,生成:
references/report-template.md- 对话摘要:数据概览 + TOP 3 问题
- Markdown 报告:,含数据概览、正常重组清单、问题诊断(按严重度降序)、过滤配置声明。严重度评级和排序规则见
recomp-analysis-YYYYMMDD-HHmm.md:总耗时(重组次数 × 平均单次耗时)为第一排序维度,次数多但单次耗时极低的问题排在真正耗时高的问题之后。references/detection-rules.md
报告写作规范(必须遵守):
- 数据概览的帧统计:只写帧数,不写占比。例如「慢帧:14 帧」,不写「14 帧(占 7.4%)」。
- 问题描述禁止使用内部术语:不得在问题描述中出现 、
RULE-A、RULE-B、RULE-C等字眼。用用户能理解的语言描述,例如「每次滚动都触发该组件重渲染」而不是「命中 RULE-B」。规则标识只允许出现在过滤配置声明段。RULE-SCOPE - 上下文描述要区分触发来源:描述重组次数时必须明确是「一次点击触发 N 次重组」还是「N 帧滚动累计触发 M 次重组」,两者不可混用。若是跨多帧的累计,说明「在 X 帧滚动过程中,该组件共重组 N 次」。
- 每个问题必须包含两个关键数据:① 同一 scope 触发的重组次数(或总重组次数);② 平均单次耗时(avgDurationMs)。缺少任一数据时标注「数据不足,无法评估严重程度」。
- 问题分析必须有深度:根因分析要说明「谁在写这个 State、在什么时机写、为什么频繁触发」,不能只说「State 变化导致重组」。优化建议必须提供修改前后的代码对比,并解释为什么这个改法有效,不能只给出结论。
- 原因不明时直接告知,并给出排查引导:如果某个问题(如单次耗时异常)通过现有日志和源码无法定位根因,不要猜测或给出模糊结论。直接写:「当前日志不足以确定根因,建议进一步排查」,并给出具体的排查建议,例如:
- 在该组件函数体内增加耗时打点()定位慢在哪个子操作
measureTimeMillis - 或直接说「可以告诉我,我来帮你做更深入的分析」
- 在该组件函数体内增加耗时打点(
- 重组次数高但总耗时低的组件不能省略:所有命中检测规则的组件都必须出现在报告中,不能因为总耗时低就跳过。对于重组次数明显偏高(如 >50 次)但单次耗时极低(<0.5ms)的组件:
- 仍然列入报告,标注严重度为「低」
- 说明重组次数和平均耗时
- 如果未做深入分析,明确注明「单次耗时极低,暂未深入分析,但重组次数偏高,建议关注」
- 对于次数极高的(如 >100 次),即使耗时低也应做简要根因分析(至少说明是什么 State 在驱动、是否可以减少重组次数)
- 尊重已有的 /
@Stable注解,不质疑其准确性:@Immutable- 类已标注 或
@Stable→ 编译器信任它是稳定的,不要说「标注不准确」「标注是无效的」@Immutable - 类含 /
Map/List属性但已标注lambda→ 注解覆盖了编译器推断,这是开发者的有意设计,不是错误@Immutable - Strong Skipping 已开启时,类含 lambda 属性 → lambda 自动
@Stable,引用稳定,不要说「lambda 引用稳定性取决于调用方」remember - 如果加了注解的类参数仍然高频变化,问题在调用方每次传入新实例,不是注解有问题。分析方向是调用方如何传参,而不是质疑注解
- 类已标注
- 建议使用 缓存对象时,必须分析依赖项:
remember- 禁止直接建议无 key 的 ,除非已确认对象创建不依赖任何外部状态
remember {} - 如果工厂函数内部可能读取 CompositionLocal(如主题色、字体大小、深色模式)→ 必须带正确的 key(如
remember),否则主题切换后配置不更新,造成界面 bugremember(isDarkTheme) { markdownColor() } - 如果无法确认工厂函数的内部依赖(没有读到源码)→ 不建议 ,而是建议「检查该函数是否依赖主题等外部状态后再决定是否缓存」
remember - 错误示例:— 如果
val colors = remember { markdownColor() }读了深色模式,切换主题后颜色不更新markdownColor() - 正确示例:
val isDark = isAppInDarkTheme(); val colors = remember(isDark) { markdownColor() }
- 禁止直接建议无 key 的
Read to generate:
references/report-template.md- Conversation Summary: Data overview + TOP 3 issues
- Markdown Report: , including data overview, normal recomposition list, issue diagnosis (sorted in descending order of severity), and filter configuration declaration. Severity rating and sorting rules are in
recomp-analysis-YYYYMMDD-HHmm.md: Total duration (recomposition count × average single duration) is the first sorting dimension; issues with high count but extremely low single duration are ranked after issues with real high duration.references/detection-rules.md
Report Writing Specifications (must comply):
- Frame statistics in data overview: Only write the number of frames, not the proportion. For example, "Slow frames: 14 frames", not "14 frames (7.4%)".
- Prohibit using internal terms in issue descriptions: Do not use terms like ,
RULE-A,RULE-B,RULE-Cin issue descriptions. Use language understandable to users, e.g., "This component is re-rendered every time scrolling occurs" instead of "Hit RULE-B". Rule identifiers are only allowed in the Filter Configuration Declaration section.RULE-SCOPE - Context description must distinguish trigger sources: When describing recomposition counts, must clearly state whether it is "One click triggers N recompositions" or "M recompositions triggered cumulatively during N frames of scrolling"; the two cannot be mixed. If it's cumulative across multiple frames, explain "During X frames of scrolling, this component recomposed N times in total".
- Each issue must contain two key data: ① Number of recompositions triggered by the same scope (or total recomposition count); ② Average single duration (avgDurationMs). Mark "Insufficient data, unable to evaluate severity" if either data is missing.
- Issue analysis must be in-depth: Root cause analysis must explain "Who is writing this State, when it is written, why it is triggered frequently", not just "Recomposition caused by State change". Optimization suggestions must provide code comparison before/after modification and explain why the modification works, not just give conclusions.
- Directly inform when cause is unknown and provide troubleshooting guidance: If the root cause of a problem (e.g., abnormal single duration) cannot be located with existing logs and source code, do not guess or give vague conclusions. Directly write: "Current logs are insufficient to determine the root cause, further troubleshooting is recommended", and provide specific troubleshooting suggestions, e.g.:
- Add duration tracking () inside the component function to locate which sub-operation is slow
measureTimeMillis - Or directly say "You can tell me, and I'll help you conduct a more in-depth analysis"
- Add duration tracking (
- Components with high recomposition count but low total duration cannot be omitted: All components that hit detection rules must be included in the report, cannot be skipped due to low total duration. For components with significantly high recomposition count (e.g., >50 times) but extremely low single duration (<0.5ms):
- Still include in the report, mark severity as "Low"
- Explain the recomposition count and average duration
- If no in-depth analysis is done, clearly note "Single duration is extremely low, no in-depth analysis yet, but recomposition count is high, recommended to pay attention"
- For those with extremely high count (e.g., >100 times), even if duration is low, brief root cause analysis should be done (at least explain which State is driving it and whether recomposition count can be reduced)
- Respect existing /
@Stableannotations, do not question their accuracy:@Immutable- Class is marked with or
@Stable→ Compiler trusts it is stable, do not say "marking is inaccurate" "marking is invalid"@Immutable - Class contains /
Map/Listproperties but is marked withlambda→ Annotation overrides compiler inference, this is intentional design by developers, not an error@Immutable - When Strong Skipping is enabled, class contains lambda property → lambda is automatically
@Stable, reference is stable, do not say "lambda reference stability depends on caller"remember - If parameters of an annotated class still change frequently, the problem is caller passes new instance every time, not the annotation. Analysis direction is how the caller passes parameters, not questioning the annotation
- Class is marked with
- When suggesting using to cache objects, must analyze dependencies:
remember- Prohibit directly suggesting without key, unless it is confirmed that object creation does not depend on any external state
remember {} - If the factory function may read CompositionLocal (e.g., theme color, font size, dark mode) → must have the correct key (e.g.,
remember), otherwise configuration will not update after theme switch, causing interface bugsremember(isDarkTheme) { markdownColor() } - If unable to confirm internal dependencies of the factory function (no source code read) → Do not recommend , instead suggest "Check whether the function depends on external states like theme before deciding whether to cache"
remember - Wrong example: — If
val colors = remember { markdownColor() }reads dark mode, color will not update after theme switchmarkdownColor() - Correct example:
val isDark = isAppInDarkTheme(); val colors = remember(isDark) { markdownColor() }
- Prohibit directly suggesting
形态 C:聚焦特定页面
Form C: Focus on Specific Page
用户说「我只想看 XX 页」时:
请在 profiler 面板点「重置」按钮,进入目标页面操作一遍,然后让我分析。
When user says "I only want to see page XX":
Please click the "Reset" button in the profiler panel, enter the target page and perform operations once, then let me analyze.
References
References
- — 可配置阈值默认值
references/config.md - — report.json / frames.jsonl 字段说明
references/log-format.md - — 各平台(adb/xcrun/hdc)拉取命令
references/log-retrieval.md - — 检测规则详细逻辑
references/detection-rules.md - — LazyList item 重组分析规则(闭包重建 vs 业务组件 skip、错误结论规避)
references/lazylist-rules.md - — Compose 稳定性规则(已实测验证):编译器推断规则、skip 条件、注解有效/危险场景、Profiler 中的表现差异
references/stability-rules.md - — 每条规则对应的优化方案和代码样例
references/optimization-patterns.md - — 已知限制(paramChanges 索引无参数名、不稳定类型 scope 重建等)
references/known-limitations.md - — Markdown 报告模板
references/report-template.md
- — Default configurable threshold values
references/config.md - — Field descriptions for report.json / frames.jsonl
references/log-format.md - — Pull commands for various platforms (adb/xcrun/hdc)
references/log-retrieval.md - — Detailed logic of detection rules
references/detection-rules.md - — Recomposition analysis rules for LazyList items (closure reconstruction vs business component skip, avoiding wrong conclusions)
references/lazylist-rules.md - — Compose stability rules (verified by actual tests): compiler inference rules, skip conditions, valid/dangerous scenarios of annotations, performance differences in Profiler
references/stability-rules.md - — Optimization solutions and code samples corresponding to each rule
references/optimization-patterns.md - — Known limitations (paramChanges index has no parameter name, unstable type scope reconstruction, etc.)
references/known-limitations.md - — Markdown report template
references/report-template.md