App perf diagnosis skill
行動 App 效能異常診斷(手機發熱/卡頓/耗電),全自動化流程:自動編譯 Profile、擷取 VM Service / 原生效能 API、收集 CPU Profiler 資料並靜態掃描效能反模式,支援 Flutter、React Native 等框架。From its SKILL.md
npx -y skills add AlexBobdylan/app-perf-diagnosis-skillAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
11.4 KB, ~4.5k tokens by cl100k_base, as published. Nobody here has run it
行動 App 效能異常診斷 Skill (v2.0)
當使用者提到以下關鍵字時自動觸發:
- App 發熱、手機燙、過熱
- 電池耗電快、掉電
- 畫面卡頓、Jank、掉幀
- CPU 使用率高
- WebView 效能問題
- 效能分析、Profile 模式、效能診斷
- 記憶體洩漏、Memory leak
階段 1:問題確認與環境準備
請主動向使用者確認:
- 症狀描述:發熱?卡頓?耗電?閃退?
- 問題畫面:哪個 Screen 或功能?
- 是否含 WebView:該畫面是否使用 WebView 載入網頁?
- 開發環境:Android Studio / VS Code?
確認後進入下一階段。
階段 2:自動清理並啟動 Profile 模式
2.1 自動殺掉舊的 flutter run 進程
在啟動新的 Profile 前,主動使用 run_command 先清理可能殘留的舊進程,避免 port 衝突:
Windows (PowerShell):
# 偵測並終止殘留的 flutter/dart 進程
$flutterProcs = Get-Process -Name "flutter*","dart*" -ErrorAction SilentlyContinue
if ($flutterProcs) {
Write-Host "⚠️ 偵測到 $($flutterProcs.Count) 個殘留進程,正在清理..."
$flutterProcs | Stop-Process -Force
Start-Sleep -Seconds 2
Write-Host "✅ 清理完成"
} else {
Write-Host "✅ 無殘留進程"
}
2.2 啟動 Profile 模式
主動使用 run_command 依序執行:
flutter clean
flutter pub get
flutter run --profile
在呼叫指令前,請先告知使用者即將執行的動作,以取得介面上的核准。
階段 3:擷取 VM Service 並收集 CPU 效能資料
3.1 取得 VM Service URL
成功啟動後,從終端機輸出中找到:
A Dart VM Service on [Device] is available at: http://127.0.0.1:xxxxx/yyyyyyyyy=/
向使用者提示切換到問題畫面並操作。
3.2 全自動 CPU 資料收集(具體 PowerShell 指令)
當使用者回覆準備好後,主動使用 run_command 執行以下指令。
將 $vmServiceUrl 替換為實際取得的 URL:
# ===== 步驟 A:取得 Isolate ID =====
$vmServiceUrl = "http://127.0.0.1:62895/jrpVApZ7cTM=/" # ← 替換為實際 URL
$vmResponse = Invoke-RestMethod -Uri "${vmServiceUrl}getVM" -Method Get
$mainIsolateId = ($vmResponse.result.isolates | Where-Object { $_.name -eq "main" }).id
Write-Host "✅ Main Isolate ID: $mainIsolateId"
# ===== 步驟 B:收集 CPU Samples(最近 15 秒)=====
$now = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
$startTime = $now - 15000 # 往回抓 15 秒
$cpuUrl = "${vmServiceUrl}getCpuSamples?isolateId=$mainIsolateId&timeOriginMicros=$($startTime * 1000)&timeExtentMicros=15000000"
$cpuData = Invoke-RestMethod -Uri $cpuUrl -Method Get
# ===== 步驟 C:解析 Top 10 熱點函式 =====
$functions = @{}
foreach ($sample in $cpuData.result.samples) {
$stackFrames = $sample.stack
if ($stackFrames.Count -gt 0) {
$topFrame = $stackFrames[0]
$funcName = $cpuData.result.functions[$topFrame].function.name
if ($functions.ContainsKey($funcName)) {
$functions[$funcName]++
} else {
$functions[$funcName] = 1
}
}
}
$totalSamples = $cpuData.result.samples.Count
Write-Host "`n📊 CPU 熱點 Top 10(共 $totalSamples 個 Samples):"
$functions.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 10 | ForEach-Object {
$pct = [math]::Round(($_.Value / $totalSamples) * 100, 1)
Write-Host " $($_.Key): $($_.Value) samples ($pct%)"
}
備用方案:若 VM Service API 無法直接呼叫(如防火牆阻擋),請向使用者索取 DevTools 網頁的 CPU Profiler → Bottom Up 截圖來做視覺分析。
階段 4:Log 關鍵字自動判讀
在 Profile 運行期間,主動監控終端機輸出,比對以下規則表自動判讀嚴重等級:
🔴 致命級(Critical)— 需立即處理
| Log 關鍵字 | 判定問題 | 建議動作 |
|---|---|---|
Skipped N frames!(N > 100) | 主執行緒被嚴重阻塞 | 檢查 CPU Profiler Top 函式 |
OutOfMemoryError | 記憶體溢出 | → 進入階段 6(記憶體分析) |
ANR 或 Application Not Responding | 應用無回應 | 檢查主執行緒是否有同步 I/O |
🟡 警告級(Warning)— 需關注
| Log 關鍵字 | 判定問題 | 建議動作 |
|---|---|---|
Skipped N frames!(30 < N ≤ 100) | 中度幀延遲 | 檢查 build() 方法複雜度 |
MutationObserver 反覆出現 | WebView DOM 監聽堆疊 | → 進入階段 5 反模式掃描 |
setState() called after dispose() | 生命週期管理錯誤 | 加入 if (!mounted) return; |
metadata version X.X.0, expected Y.Y.0 | Kotlin 版本衝突 | 鎖定 resolutionStrategy |
🟢 資訊級(Info)— 可忽略
| Log 關鍵字 | 判定問題 |
|---|---|
Skipped N frames!(N ≤ 30) | 輕微卡頓,通常可接受 |
Reloaded X of Y libraries | 正常 Hot Reload |
階段 5:全自動原始碼掃描(擴大版反模式清單)
主動使用 grep_search 對問題畫面原始碼及相關檔案進行掃描。
5.1 🔴 WebView 致命反模式
| 搜尋關鍵字 | 反模式說明 | 嚴重度 |
|---|---|---|
runJavaScript + querySelectorAll('*') | DOM 全遍歷,每次觸發序列化上千個元素 | 🔴 致命 |
Timer.periodic + runJavaScript | 定時器不斷注入 JS,CPU 持續高負載 | 🔴 致命 |
setInterval 在注入的 JS 中 | JS 層的定時器與 Dart 定時器疊加 | 🔴 致命 |
new MutationObserver 重複注入 | 每次注入都新增一個 Observer | 🔴 致命 |
標準修復模板:
/// ✅ 輕量版 CSS 注入(只注入一次,用 ID 防重複)
void _injectStyleOnce() {
_controller.runJavaScript('''
if (!document.getElementById('app-fix-style')) {
var style = document.createElement('style');
style.id = 'app-fix-style';
style.textContent = `* { font-size: 12px !important; }`;
document.head.appendChild(style);
}
''');
}
5.2 🟡 Widget 重建過度
| 搜尋關鍵字 | 反模式說明 | 嚴重度 |
|---|---|---|
setState 在 Timer.periodic 回呼中 | 高頻整頁重建 | 🟡 警告 |
setState 在 StreamBuilder 的 listen 中 | 應使用 StreamBuilder 而非手動 setState | 🟡 警告 |
整個 build() 方法超過 200 行 | 單一 Widget 過度龐大 | 🟡 警告 |
5.3 🟡 資源未釋放(記憶體洩漏源)
| 搜尋關鍵字 | 反模式說明 | 嚴重度 |
|---|---|---|
StreamSubscription 無對應 .cancel() | Stream 訂閱未關閉 | 🟡 警告 |
AnimationController 無對應 .dispose() | 動畫控制器未釋放 | 🟡 警告 |
Timer.periodic 無對應 .cancel() | 定時器未在 dispose() 中清除 | 🟡 警告 |
ScrollController 無對應 .dispose() | 捲軸控制器未釋放 | 🟡 警告 |
TextEditingController 無對應 .dispose() | 文字輸入控制器未釋放 | 🟡 警告 |
FocusNode 無對應 .dispose() | 焦點節點未釋放 | 🟡 警告 |
5.4 🟡 圖片與資源效能
| 搜尋關鍵字 | 反模式說明 | 嚴重度 |
|---|---|---|
Image.network 無 cacheWidth / cacheHeight | 大圖未降解析度,佔用大量記憶體 | 🟡 警告 |
Image.asset 載入超過 2MB 的圖片 | 資源圖片過大 | 🟡 警告 |
BoxDecoration + DecorationImage 無 resize | 背景圖未最佳化 | 🟢 建議 |
階段 6:記憶體分析(Memory Profiling)
如果 CPU 分析未發現明顯問題,或症狀包含「越用越卡」「長時間使用後閃退」,則進入記憶體分析。
6.1 使用 VM Service API 取得記憶體分配
主動使用 run_command 執行:
# ===== 取得記憶體分配概況 =====
$vmServiceUrl = "http://127.0.0.1:62895/jrpVApZ7cTM=/" # ← 替換為實際 URL
$mainIsolateId = "isolates/XXXXXXXXXX" # ← 使用階段 3 取得的 ID
$allocUrl = "${vmServiceUrl}getAllocationProfile?isolateId=$mainIsolateId&gc=true"
$allocData = Invoke-RestMethod -Uri $allocUrl -Method Get
# 解析記憶體摘要
$heapUsed = [math]::Round($allocData.result.memoryUsage.heapUsage / 1MB, 2)
$heapCap = [math]::Round($allocData.result.memoryUsage.heapCapacity / 1MB, 2)
$extUsed = [math]::Round($allocData.result.memoryUsage.externalUsage / 1MB, 2)
Write-Host "`n🧠 記憶體概況:"
Write-Host " Heap 使用: ${heapUsed} MB / ${heapCap} MB"
Write-Host " External: ${extUsed} MB"
Write-Host " 使用率: $([math]::Round(($heapUsed / $heapCap) * 100, 1))%"
6.2 記憶體判讀規則
| 指標 | 正常 | 需關注 | 危險 |
|---|---|---|---|
| Heap 使用率 | < 60% | 60-85% | > 85% |
| External Memory | < 50 MB | 50-150 MB | > 150 MB |
| 持續增長趨勢 | 穩定平坦 | 緩慢上升 | 持續攀升不回落 |
階段 7:產出標準化診斷報告
結合所有分析結果,主動產出 Artifact 報告(檔名:perf_diagnosis_report.md)。
📋 報告固定範本:
# 📋 效能診斷報告
**診斷日期:** YYYY-MM-DD
**診斷人員:** [開發者姓名]
**目標畫面:** [畫面名稱 / Screen 檔案路徑]
**診斷工具:** Flutter DevTools (CPU Profiler + Memory)
**App 版本:** [版本號]
---
## 一、問題摘要
| 項目 | 內容 |
|------|------|
| 使用者回報症狀 | [發熱 / 卡頓 / 耗電 / 閃退] |
| 根因判定 | [一句話描述根本原因] |
| 嚴重等級 | [🔴 致命 / 🟡 警告 / 🟢 輕微] |
---
## 二、CPU 分析數據
### Top 5 熱點函式
| 排名 | 函式名稱 | Self Time | 佔比 | 歸因 |
|------|---------|-----------|------|------|
| 1 | [函式名] | [時間] | [XX%] | [WebView通訊/Widget重建/佈局計算] |
| 2 | ... | ... | ... | ... |
### 判讀結論
[根據 CPU 數據的結論性分析]
---
## 三、記憶體分析數據(如適用)
| 指標 | 數值 | 狀態 |
|------|------|------|
| Heap 使用 | XX MB / XX MB | [正常/警告/危險] |
| External | XX MB | [正常/警告/危險] |
| 趨勢 | [穩定/上升/持續攀升] | — |
---
## 四、原始碼問題定位
### 問題 1:[問題標題]
- **檔案:** `[檔案路徑]`
- **行號:** L[XX] - L[XX]
- **嚴重度:** [🔴/🟡/🟢]
- **問題程式碼:**
```dart
// ❌ 問題程式碼
- 修復建議:
// ✅ 修復後程式碼
五、修復前後對比
| 指標 | 修復前 | 修復後 | 改善幅度 |
|---|---|---|---|
| WebView CPU 佔比 | XX% | XX% | ↓XX% |
| FPS | XX fps | XX fps | ↑XX fps |
| 體感溫度 | 異常發熱 | 正常 | ✅ |
六、後續建議
- [建議事項 1]
- [建議事項 2]
- [建議事項 3]
---
## 附錄:Kotlin 版本衝突速查
若編譯階段遇到 `metadata version` 錯誤,在 `android/build.gradle` 的 `allprojects` 加入:
```gradle
configurations.all {
resolutionStrategy {
force "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
}
What ships with it: 2 files
6.2 KB alongside SKILL.md