$npx -y skills add rcosteira79/android-skills --skill android-debuggingUse when debugging Android or KMP issues — Android-specific techniques covering Logcat, ADB, ANR traces, R8 stack trace decoding, memory leaks, Gradle build failures, and Compose recomposition bugs, on a root-cause-first foundation.
| 1 | # Android Debugging |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Android-specific evidence-gathering and investigation techniques on a root-cause-first foundation. |
| 6 | |
| 7 | **Root-cause-first foundation:** |
| 8 | - **No fix before the root cause is found.** Investigate first — reproduce, gather evidence (observed values, not assumed), trace the cause. Patching a symptom you don't understand creates two bugs. |
| 9 | - **Three failed guesses ⇒ stop and question the architecture** rather than guessing again. |
| 10 | |
| 11 | The Android-specific tools below serve that investigation. *Optional:* if you run a dedicated debugging-discipline skill (e.g. `superpowers:systematic-debugging` or `ace:systematic-debugging`), this layers on top of it — but it requires none. |
| 12 | |
| 13 | ## Evidence-Gathering by Problem Type |
| 14 | |
| 15 | ### Crashes & Exceptions |
| 16 | |
| 17 | ```bash |
| 18 | # Stream crash logs filtered by app package |
| 19 | adb logcat --pid=$(adb shell pidof -s com.example.app) |
| 20 | |
| 21 | # Save full logcat to file for analysis |
| 22 | adb logcat -d > crash_log.txt |
| 23 | |
| 24 | # Filter by tag |
| 25 | adb logcat -s "YourTag:E" |
| 26 | ``` |
| 27 | |
| 28 | Key logcat log levels: `V` (verbose) `D` (debug) `I` (info) `W` (warn) `E` (error) `F` (fatal) |
| 29 | |
| 30 | Read the **full stack trace** — the root cause is usually at the bottom of the `Caused by:` chain, not the top-level exception. |
| 31 | |
| 32 | ### ANR (Application Not Responding) |
| 33 | |
| 34 | ANRs mean the main thread was blocked. Evidence: |
| 35 | |
| 36 | ```bash |
| 37 | # Pull ANR trace from device |
| 38 | adb pull /data/anr/traces.txt ./anr_traces.txt |
| 39 | |
| 40 | # Or stream while reproducing |
| 41 | adb logcat -s "ActivityManager:E" | grep -A 30 "ANR in" |
| 42 | ``` |
| 43 | |
| 44 | Look for: `main` thread in state `MONITOR` (waiting for a lock) or blocking I/O on `main`. Trace backward to find what holds the lock. |
| 45 | |
| 46 | **Common causes:** Database/network call on main thread, `runBlocking` on main thread, deadlock between coroutine scopes. |
| 47 | |
| 48 | ### Memory Leaks |
| 49 | |
| 50 | Add [LeakCanary](https://square.github.io/leakcanary/) to `debugImplementation`. It surfaces leak traces automatically in a notification. |
| 51 | |
| 52 | Read the leak trace top-to-bottom: the first bold line is the leaking object, the path shows what's holding the reference. Fix by clearing the reference in the appropriate lifecycle callback. |
| 53 | |
| 54 | ```bash |
| 55 | # Dump heap manually for Android Profiler analysis |
| 56 | adb shell am dumpheap com.example.app /data/local/tmp/heap.hprof |
| 57 | adb pull /data/local/tmp/heap.hprof ./heap.hprof |
| 58 | ``` |
| 59 | |
| 60 | ### Performance Trace Investigation (Perfetto) |
| 61 | |
| 62 | For bottleneck investigation across CPU, graphics, I/O, IPC, memory, or power — beyond what Logcat and ANR traces show — capture a Perfetto trace and query it with SQL. |
| 63 | |
| 64 | **Measure before you fix.** For performance regressions, logs usually mislead — capture a *baseline* measurement before changing anything, then bisect against it. A trace tells you where time goes; only a comparison against a known-good baseline tells you what actually regressed. |
| 65 | |
| 66 | ```bash |
| 67 | # Capture a trace (system-level, all categories) |
| 68 | adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/trace.pftrace \ |
| 69 | <<'EOF' |
| 70 | buffers { size_kb: 65536 } |
| 71 | data_sources { config { name: "linux.ftrace" } } |
| 72 | data_sources { config { name: "android.surfaceflinger.frame" } } |
| 73 | duration_ms: 10000 |
| 74 | EOF |
| 75 | |
| 76 | adb pull /data/misc/perfetto-traces/trace.pftrace ./ |
| 77 | ``` |
| 78 | |
| 79 | Then open the trace at https://ui.perfetto.dev and run SQL against it (`SELECT name, dur FROM slice WHERE dur > 16e6` for frames slower than 16ms, etc.). |
| 80 | |
| 81 | For an agent-driven workflow — translating an investigation intent (jank, slow startup, battery drain) into the right Perfetto SQL and iterating across the trace — see Google's [`perfetto-sql`](https://github.com/android/skills/tree/main/profilers/perfetto-sql) and [`perfetto-trace-analysis`](https://github.com/android/skills/tree/main/profilers/perfetto-trace-analysis) skills (`android skills list` to check for a local install; `android skills add perfetto-sql perfetto-trace-analysis` otherwise). They provide Domain Hints (CPU/Graphics/I/O/IPC/Memory/Power), a mandatory scratchpad chain-of-evidence pattern, and `GLOB`-over-`LIKE` query rules. |
| 82 | |
| 83 | ### R8 / ProGuard — Obfuscated Stack Traces |
| 84 | |
| 85 | Release crash stack traces are obfuscated. Decode them with the mapping file generated at build time. |
| 86 | |
| 87 | ```bash |
| 88 | # retrace a crash (AGP 7+) |
| 89 | ./gradlew :app:retrace --stacktrace-file crash.txt |
| 90 | |
| 91 | # Or use the retrace CLI directly |
| 92 | java -jar retrace.jar mapping.txt crash.txt |
| 93 | ``` |
| 94 | |
| 95 | Mapping files are in `app/build/outputs/mapping/<variant>/mapping.txt`. Always archive them alongside release builds. |
| 96 | |
| 97 | If a class is unexpectedly removed or renamed, add a `-keep` rule in `proguard-rules.pro` and verify with: |
| 98 | |
| 99 | ```bash |
| 100 | ./gradlew :app:assembleRelease |
| 101 | # Then inspect: app/build/outputs/mapping/release/usage.txt (removed) and seeds.txt (kept) |
| 102 | ``` |
| 103 | |
| 104 | For the **inverse problem** — reading obfuscated third-party code or decoding a stack tra |