Performance cheat sheet
The 12 Performance interview answers in 30 seconds each, plus quick facts. Printable, free.
One page per topic: print it or save it as PDF from your browser's print menu. Practise these questions
Rules of thumb
- Nothing slow on the main thread: no disk, network or locks around I/O.
- Measure release builds on mid-range devices.
- Perfetto for jank, Macrobenchmark for numbers, LeakCanary for leaks.
- Ship a Baseline Profile and keep it updated.
- App Bundle plus R8 for size.
Looper, Handler and the main thread
The main thread runs a Looper: an endless loop that takes the next Message from its MessageQueue and runs it. Input events, lifecycle callbacks, drawing (via Choreographer) and your posted Runnables are all messages on that queue. A Handler posts messages to a specific Looper. If one message takes too long, everything behind it waits, which shows up as jank or an ANR.
ANRs: causes and fixes
An ANR (Application Not Responding) is reported when the main thread can't respond in time: 5 seconds for an input event, 10 seconds for a foreground broadcast's onReceive (60 in the background), and limits for services that start too slowly. Common causes are disk or network on main, lock contention, runBlocking and slow onReceive work. Android vitals in Play Console groups them with main-thread stack traces.
Jank and dropped frames
Jank is when a frame isn't ready in time: 16.7 ms at 60 Hz, 8.3 ms at 120 Hz. Causes include heavy work on the main thread, expensive layout or recomposition, large bitmaps decoded on main, overdraw and GC pauses from allocation. Find it with JankStats or Macrobenchmark's FrameTimingMetric for numbers, and a Perfetto system trace to see what each slow frame was doing.
Memory leaks
A leak happens when something long-lived keeps a reference to something that should have been freed, usually an Activity, Fragment view or Context. Typical causes: a singleton or static field holding an Activity, listeners not unregistered, coroutines or callbacks that capture a view and outlive it, and Fragment bindings kept after onDestroyView. LeakCanary finds them automatically in debug builds with the reference chain.
Cold start time
Measure time to initial display (TTID) and time to full display (TTFD, reported with reportFullyDrawn()) with Macrobenchmark's StartupTimingMetric on a release build. Then cut work on the critical path: lazy-initialise SDKs, avoid disk and network before the first frame, simplify the first screen, and ship a Baseline Profile so ART precompiles startup code.
Baseline Profiles
A Baseline Profile is a list of classes and methods your app uses on critical paths like startup and scrolling. It ships inside the app, and at install ART compiles that code ahead of time instead of interpreting and JIT-compiling it on first use. Google reports first-launch speed-ups of around 30% for many apps. You generate it with the Baseline Profile Gradle plugin and a Macrobenchmark journey.
Smooth long lists
Give items stable keys and content types so the list reuses compositions, keep item composables cheap (no sorting or formatting during composition), load images at the displayed size with a caching loader, avoid reading fast-changing state in item bodies, and precompute UI models in the ViewModel. Then measure with a release build and FrameTimingMetric.
StrictMode
StrictMode is a developer tool that detects mistakes as they happen: disk reads and writes or network on the main thread (thread policy), and leaked closeables, Activities and unsafe intents (VM policy). Turn it on in debug builds at startup with penaltyLog() or penaltyDeath() so problems show up before users hit them.
Profiling tools
Android Studio's profilers cover CPU (method and system traces), memory (heap dumps, allocations) and network. Perfetto system traces show every thread, frame, Binder call and lock, which is best for jank and startup. Layout Inspector shows recomposition counts. Macrobenchmark measures startup and frames in CI; Microbenchmark measures small functions. Android vitals shows real-world crashes, ANRs and slow starts.
Macrobenchmark and Microbenchmark
Macrobenchmark tests whole user journeys from outside the app, such as cold start or scrolling, on a real device or emulator, reporting metrics like StartupTimingMetric and FrameTimingMetric. It runs against a release-like build. Microbenchmark measures small pieces of code in a loop, like a parser or a sort, handling warm-up and clock stability. Use macro for user-facing speed, micro for hot functions.
Reducing app size
Ship an Android App Bundle so Play delivers only the code and resources for each device's ABI, density and language. Enable R8 with isMinifyEnabled and isShrinkResources to remove unused code and resources. Use vector drawables and WebP, remove unused libraries, and move rarely used features or large assets to on-demand delivery with Play Feature Delivery or Play Asset Delivery.
Battery-friendly apps
The biggest costs are keeping the radio, GPS or CPU awake: frequent network polling, wake locks held too long, high-accuracy location in the background, and work that ignores Doze. Batch work with WorkManager and constraints, use push instead of polling, request the lowest location accuracy and update rate that works, and release wake locks promptly.
Quick facts
- Frame budget
- At 60 Hz each frame has 16.7 ms; at 90 Hz 11.1 ms; at 120 Hz 8.3 ms.
- Input ANR timeout
- An ANR is reported when an input event gets no response within 5 seconds.
- reportFullyDrawn()
- Calling reportFullyDrawn() tells the system when your content is ready, giving time to full display.
- profileable
- <profileable android:shell="true"/> lets profilers attach to a release build.
- LeakCanary
- LeakCanary watches destroyed Activities, Fragments and ViewModels and reports any that aren't garbage-collected.