AI Skill Report Card

Debugging and Optimizing Android Apps

A88·Aug 2, 2026·Source: Web
Markdown
--- name: debugging-optimizing-android-apps description: Diagnoses and fixes Android app crashes, performance jank, memory leaks, and scalability bottlenecks using Android Studio Profiler, ADB, Logcat, and libraries like LeakCanary and Glide. Use when an app is slow, stuttering, crashing, leaking memory, or needs to be validated for scale before release. --- # Debugging and Optimizing Android Apps
14 / 15

When a bug report says "app feels slow/laggy/crashes," don't guess — gather evidence first:

Bash
# 1. Confirm debug build & attach device adb devices adb logcat | grep -E "Choreographer|AndroidRuntime|OutOfMemory" # 2. Look for dropped frames (jank indicator) # I/Choreographer: Skipped 42 frames! The application may be doing too much work on its main thread. # 3. Attach Android Studio Profiler (CPU, Memory, Network) to the running process # 4. Attach LeakCanary if OOM or memory growth is suspected (auto-detects on debug builds)

If Logcat shows skipped frames + Profiler shows CPU spikes tied to a specific UI event (e.g., scroll, image load) → main-thread blocking work is the likely root cause. Proceed to Workflow.

Recommendation
Add a concrete before/after code snippet (e.g., Coroutine offload of Bitmap decode) to make fixes more directly actionable
15 / 15

Progress:

  • Step 1: Reproduce & isolate (Logcat + user report + Profiler)
  • Step 2: Pinpoint the exact slow call (Method Tracing / breakpoints)
  • Step 3: Fix — offload, cache, or restructure
  • Step 4: Check memory lifecycle (LeakCanary)
  • Step 5: Verify with Profiler (frame rate, sawtooth memory pattern)
  • Step 6: Automate regression coverage (Espresso/UI Automator + Firebase Test Lab)
  • Step 7: Document the fix as a project best-practice note

Step 1: Reproduce & Isolate

  • Enable developer options + USB debugging on a physical device (emulators hide real perf issues).
  • Confirm the build variant is debuggable.
  • Read Logcat for the failure signature:
    • Skipped N frames → main thread jank
    • OutOfMemoryError / repeated GC logs → memory pressure
    • Stack trace with FATAL EXCEPTION → crash, go straight to breakpoint debugging
  • Correlate with the Profiler timeline (CPU/Memory/Network) — trigger the exact user action (e.g., scroll feed) while recording.

Step 2: Pinpoint the Exact Slow Call

  • Use Method Tracing in the CPU Profiler to get per-function timing.
  • Budget math: 60fps = 16ms/frame. Anything eating >16ms on the main thread during a frame is a jank source.
  • Set line breakpoints around suspect code; use Step Over/Into/Out to inspect the object tree and confirm what's actually executing (e.g., Bitmap.createScaledBitmap taking 80ms).
  • For crashes: attach debugger to the running process (or launch in debug mode), reproduce the crash, inspect variable state at the breakpoint just before the exception.

Step 3: Fix

Match the fix to the diagnosis:

SymptomFix
Heavy work on main thread (image resize, JSON parsing, disk I/O)Move to Kotlin Coroutines / background dispatcher
Repeated expensive work (image decode, network calls)Add caching layer (Glide/Coil for images — auto downsampling + memory cache)
Large lists redrawing/rebinding constantlyUse RecyclerView DiffUtil, avoid notifyDataSetChanged()
Memory leak on screen exitFix lifecycle observers; clear references (image loaders, listeners, static contexts)
Slow builds / dependency bloatAudit Gradle config, modularize, enable build caching

Step 4: Memory Lifecycle Check

  • Run LeakCanary through a full navigate-away cycle (open feed → leave screen → back → repeat).
  • Confirm no retained Activity/Fragment/Bitmap references in the leak trace.
  • Fix by clearing image requests / unregistering observers in onDestroy/onCleared.

Step 5: Verify

  • Re-run Profiler: Logcat should no longer show skipped frames; frame rate should hold steady (~60fps).
  • Memory graph should show a sawtooth pattern (allocate → GC → clear), not a staircase (constant growth = still leaking).

Step 6: Automate Regression Coverage

  • Write instrumented UI tests in src/androidTest/java (Espresso/UI Automator) simulating the exact interaction that caused jank (e.g., rapid scroll through 1,000 items).
  • Keep tests hermetic — no live network/account dependencies — so they run reliably in CI (Trade Federation or similar harness).
  • Run the matrix on Firebase Test Lab across device types, OS versions, and orientations before shipping.

Step 7: Document

Add a one-line best-practice note to project docs (e.g., PERFORMANCE.md) so the fix becomes a standing rule, not a one-off patch. Example: "All image processing must be downsampled via Glide and never executed on the Main Dispatcher."

Recommendation
Include an example of a bad/failed fix attempt to illustrate common missteps in diagnosis
18 / 20

Example 1: Janky Scroll Feed Input: User reports laggy scrolling on social feed; Logcat shows Skipped 42 frames; Profiler shows CPU spikes on each new image bind. Output: Root cause = Bitmap.createScaledBitmap (80ms) running synchronously in onBindViewHolder. Fix = move decode/resize off main thread via Coroutines, replace manual bitmap handling with Glide (auto-downsampling + caching). Result: no more skipped-frame logs, steady 60fps, sawtooth memory pattern in Profiler.

Example 2: OOM Crash on Screen Rotation Input: App crashes with OutOfMemoryError after repeatedly rotating a screen with large images. Output: LeakCanary trace shows retained Activity reference held by an image-loading callback. Fix = cancel/clear the image request in onDestroy, use lifecycle-aware loading (Glide's with(lifecycleOwner)). Verified via repeated rotate cycles with no leak reported.

Example 3: Slow API-Driven Screen Input: Network Profiler shows a screen making 5 sequential API calls before rendering, each blocking the next. Output: Refactor to parallel Coroutine calls (async/awaitAll), add response caching for repeat visits. Load time drops from ~2.5s to ~600ms.

Recommendation
Consider adding guidance on interpreting specific Profiler screenshots or metrics thresholds beyond the 16ms frame budget
  • Always profile on a physical device — emulator performance is misleading.
  • Never do bitmap decoding, JSON parsing, or file I/O on the main thread — default to background dispatchers.
  • Prefer established libraries (Glide/Coil, Room, WorkManager) over hand-rolled caching/threading — they handle edge cases you'll otherwise rediscover the hard way.
  • Treat "Skipped frames" in Logcat as a hard signal, not noise — investigate immediately.
  • Keep instrumented tests hermetic (no network/account dependencies) so CI results are trustworthy.
  • Validate fixes across a device/OS matrix (Firebase Test Lab) before considering the job done — a fix on one device isn't a fix.
  • Document every non-obvious fix as a project rule so the same bug class doesn't recur.
  • Guessing at the fix before profiling. Always reproduce and measure first — "feels slow" isn't a diagnosis.
  • Testing only on emulators or one high-end device. Scale/perf issues often only show up on mid/low-tier hardware.
  • Fixing symptoms, not lifecycle causes. Clearing a leak once without fixing the lifecycle observer means it recurs on the next screen.
  • Skipping the memory check after a performance fix. A faster app that leaks is still not production-ready.
  • Writing UI tests with real network/account dependencies. These become flaky and erode trust in CI.
  • Manually resizing/caching images instead of using Glide/Coil. Reinventing this almost always misses downsampling or cache invalidation edge cases.
0
Grade AAI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
15/15
Examples
18/20
Completeness
19/20
Format
15/15
Conciseness
13/15