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.