[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FDNSMFD753DZ7J799WEGWC":3,"skill-same:01M6FDNSMFD753DZ7J799WEGWC":49},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":22,"linkOnly":23,"markdownContent":24,"markdownBody":24,"frontMatter":25,"refName":26,"commitSha":27,"skillDirectory":28,"forksCount":29,"repositoryHtmlUrl":30,"repositoryHomepageUrl":31,"ownerHtmlUrl":32,"ownerBlogUrl":6,"ownerDescription":33,"updatedAt":34,"install":35,"resources":48},"01M6FDNSMFD753DZ7J799WEGWC","compose-performance-audit",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fnew-silvermoon\u002Fawesome-android-agent-skills\u002Fcompose-performance-audit\u002FSKILL.md",[],[18,19,20,21],"performance","audit","compose","compose performance audit","2026-11-10 10:12:30",false,"# Compose Performance Audit\n\n## Overview\n\nAudit Jetpack Compose view performance end-to-end, from instrumentation and baselining to root-cause analysis and concrete remediation steps.\n\n## Workflow Decision Tree\n\n- If the user provides code, start with \"Code-First Review.\"\n- If the user only describes symptoms, ask for minimal code\u002Fcontext, then do \"Code-First Review.\"\n- If code review is inconclusive, go to \"Guide the User to Profile\" and ask for Layout Inspector output or Perfetto traces.\n\n## 1. Code-First Review\n\nCollect:\n- Target Composable code.\n- Data flow: state, remember, derived state, ViewModel connections.\n- Symptoms and reproduction steps.\n\nFocus on:\n- **Recomposition storms** from unstable parameters or broad state changes.\n- **Unstable keys** in `LazyColumn`\u002F`LazyRow` (`key` churn, missing keys).\n- **Heavy work in composition** (formatting, sorting, filtering, object allocation).\n- **Unnecessary recompositions** (missing `remember`, unstable classes, lambdas).\n- **Large images** without proper sizing or async loading.\n- **Layout thrash** (deep nesting, intrinsic measurements, `SubcomposeLayout` misuse).\n\nProvide:\n- Likely root causes with code references.\n- Suggested fixes and refactors.\n- If needed, a minimal repro or instrumentation suggestion.\n\n## 2. Guide the User to Profile\n\nExplain how to collect data:\n- Use **Layout Inspector** in Android Studio to see recomposition counts.\n- Enable **Recomposition Highlights** in Compose tooling.\n- Use **Perfetto** or **System Trace** for frame timing analysis.\n- Check **Macrobenchmark** results for startup\u002Fscroll metrics.\n\nAsk for:\n- Layout Inspector screenshot showing recomposition counts.\n- Perfetto trace or System Trace export.\n- Device\u002FOS\u002Fbuild configuration (debug vs release).\n\n> **Important**: Ensure profiling is done on a **release build** with R8 enabled. Debug builds have significant overhead.\n\n## 3. Analyze and Diagnose\n\nPrioritize likely Compose culprits:\n- **Recomposition storms** from unstable parameters or broad state changes.\n- **Unstable keys** in lazy lists (`key` churn, index-based keys).\n- **Heavy work in composition** (formatting, sorting, object allocation).\n- **Missing `remember`** causing recreations on every recomposition.\n- **Large images** without `Modifier.size()` constraints.\n- **Unnecessary state reads** in wrong composition phases.\n\nSummarize findings with evidence from traces\u002FLayout Inspector.\n\n## 4. Remediate\n\nApply targeted fixes:\n- **Stabilize parameters**: Use `@Stable` or `@Immutable` annotations on data classes.\n- **Stabilize keys**: Use stable, unique IDs for `LazyColumn`\u002F`LazyRow` items.\n- **Defer state reads**: Use `derivedStateOf`, lambda-based modifiers, or `Modifier.drawBehind`.\n- **Remember expensive computations**: Wrap in `remember { }` or `remember(key) { }`.\n- **Skip recomposition**: Extract stable composables, use `key()` to control identity.\n- **Async image loading**: Use Coil\u002FGlide with proper sizing constraints.\n- **Reduce layout complexity**: Flatten hierarchies, avoid deep nesting.\n\n## Common Code Smells (and Fixes)\n\n### Unstable lambda captures\n\n```kotlin\n\u002F\u002F BAD: New lambda instance every recomposition\nButton(onClick = { viewModel.doSomething(item) }) { ... }\n\n\u002F\u002F GOOD: Use remember or method reference\nval onClick = remember(item) { { viewModel.doSomething(item) } }\nButton(onClick = onClick) { ... }\n```\n\n### Expensive work in composition\n\n```kotlin\n\u002F\u002F BAD: Sorting on every recomposition\n@Composable\nfun ItemList(items: List\u003CItem>) {\n    val sorted = items.sortedBy { it.name } \u002F\u002F Runs every recomposition\n    LazyColumn { items(sorted) { ... } }\n}\n\n\u002F\u002F GOOD: Use remember with key\n@Composable\nfun ItemList(items: List\u003CItem>) {\n    val sorted = remember(items) { items.sortedBy { it.name } }\n    LazyColumn { items(sorted) { ... } }\n}\n```\n\n### Missing keys in LazyColumn\n\n```kotlin\n\u002F\u002F BAD: Index-based identity (causes recomposition on list changes)\nLazyColumn {\n    items(items) { item -> ItemRow(item) }\n}\n\n\u002F\u002F GOOD: Stable key-based identity\nLazyColumn {\n    items(items, key = { it.id }) { item -> ItemRow(item) }\n}\n```\n\n### Unstable data classes\n\n```kotlin\n\u002F\u002F BAD: Unstable (contains List, which is not stable)\ndata class UiState(\n    val items: List\u003CItem>,\n    val isLoading: Boolean\n)\n\n\u002F\u002F GOOD: Mark as Immutable if truly immutable\n@Immutable\ndata class UiState(\n    val items: ImmutableList\u003CItem>, \u002F\u002F kotlinx.collections.immutable\n    val isLoading: Boolean\n)\n```\n\n### Reading state too early\n\n```kotlin\n\u002F\u002F BAD: State read during composition (recomposes whole tree)\n@Composable\nfun AnimatedBox(scrollState: ScrollState) {\n    val offset = scrollState.value \u002F\u002F Recomposes on every scroll\n    Box(modifier = Modifier.offset(y = offset.dp)) { ... }\n}\n\n\u002F\u002F GOOD: Defer state read to layout\u002Fdraw phase\n@Composable\nfun AnimatedBox(scrollState: ScrollState) {\n    Box(modifier = Modifier.offset {\n        IntOffset(0, scrollState.value) \u002F\u002F Read in layout phase\n    }) { ... }\n}\n```\n\n### Object allocation in composition\n\n```kotlin\n\u002F\u002F BAD: Creates new Modifier chain every recomposition\nBox(modifier = Modifier.padding(16.dp).background(Color.Red))\n\n\u002F\u002F GOOD for dynamic modifiers: Remember the modifier\nval modifier = remember { Modifier.padding(16.dp).background(Color.Red) }\nBox(modifier = modifier)\n```\n\n## Stability Checklist\n\n| Type | Stable by Default? | Fix |\n|------|-------------------|-----|\n| Primitives (`Int`, `String`, `Boolean`) | Yes | N\u002FA |\n| `data class` with stable fields | Yes* | Ensure all fields are stable |\n| `List`, `Map`, `Set` | **No** | Use `ImmutableList` from kotlinx |\n| Classes with `var` properties | **No** | Use `@Stable` if externally stable |\n| Lambdas | **No** | Use `remember { }` |\n\n## 5. Verify\n\nAsk the user to:\n- Re-run Layout Inspector and compare recomposition counts.\n- Run Macrobenchmark and compare frame timing.\n- Test on a real device with release build.\n\nSummarize the delta (recomposition count, frame drops, jank) if provided.\n\n## Outputs\n\nProvide:\n- A short metrics table (before\u002Fafter if available).\n- Top issues (ordered by impact).\n- Proposed fixes with estimated effort.\n\n## References\n\n- [Jetpack Compose Performance](https:\u002F\u002Fdeveloper.android.com\u002Fdevelop\u002Fui\u002Fcompose\u002Fperformance)\n- [Compose Stability Explained](https:\u002F\u002Fdeveloper.android.com\u002Fdevelop\u002Fui\u002Fcompose\u002Fperformance\u002Fstability)\n- [Debugging Recomposition](https:\u002F\u002Fdeveloper.android.com\u002Fdevelop\u002Fui\u002Fcompose\u002Ftooling\u002Flayout-inspector)\n- [Macrobenchmark](https:\u002F\u002Fdeveloper.android.com\u002Ftopic\u002Fperformance\u002Fbenchmarking\u002Fmacrobenchmark-overview)\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fnew-silvermoon\u002Fawesome-android-agent-skills\u002Fcompose-performance-audit",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill compose-performance-audit",{"tool":41,"command":42},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill compose-performance-audit",{"tool":44,"command":45},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill compose-performance-audit","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fnew-silvermoon\u002Fawesome-android-agent-skills\u002Fcompose-performance-audit\u002FSKILL.md",true,[],[50,64,74,83,89,96],{"skillNo":51,"name":52,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":53,"categories":54,"keywords":60,"publishedAt":22},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[55],{"categoryNo":56,"categoryName":57,"taxonomyType":58,"taxonomyTypeDesc":59},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[61,62,63],"model","model hierarchy","model hierarchy skill",{"skillNo":65,"name":66,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":70,"publishedAt":22},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[69],{"categoryNo":56,"categoryName":57,"taxonomyType":58,"taxonomyTypeDesc":59},[71,72,73],"MCP","zoom","zoom mcp",{"skillNo":75,"name":76,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":79,"publishedAt":22},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[80,81,82,72],"sdk","zoom apps sdk","apps",{"skillNo":84,"name":85,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":22},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[85],{"skillNo":90,"name":91,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":95,"publishedAt":22},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[94],{"categoryNo":56,"categoryName":57,"taxonomyType":58,"taxonomyTypeDesc":59},[91],{"skillNo":97,"name":98,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":22},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[98]]