Performance optimization without measurement is guesswork. This article covers the tools and techniques that actually move the needle — from diagnosing startup time to eliminating jank in scrolling lists.
Step 1: Measure First
Before changing any code, establish a baseline with reproducible benchmarks. The Macrobenchmark library lets you measure startup time, scrolling performance, and custom scenarios in a controlled way.
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun startup() = benchmarkRule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
) {
pressHome()
startActivityAndWait()
}
}
App Startup Optimization
- Baseline Profiles — provide a
baseline-prof.txtfile that guides ART to AOT-compile your hot code paths. Can reduce cold start by 30–40%. - Lazy initialization — defer anything not needed on the first frame. Use
App Startuplibrary to sequence initializers. - Avoid heavy work on the main thread — database reads, SharedPreferences access, and network calls must all move off the main thread.
Eliminating Jank (Dropped Frames)
The Android Profiler's CPU view and the Systrace tool reveal exactly where frames drop. Common causes:
- Layout inflation during scrolling — use
RecyclerViewprefetching or Compose'sLazyColumnwith stable keys. - Large bitmaps decoded on the main thread — use Coil or Glide which decode off-thread by default.
- Expensive
onDraw()implementations — avoid object allocation inside draw calls. - Nested
ConstraintLayoutor deep view hierarchies — flatten withConstraintLayoutor migrate to Compose.
Memory Optimization
- Use the Memory Profiler to spot allocation spikes and leaks. Look for retained objects in the heap dump.
- Avoid static references to Context or Views — they prevent garbage collection.
- Use
WeakReferenceonly when strictly necessary; prefer lifecycle-aware components instead.
Network and Battery
- Batch network requests and use caching (OkHttp cache or Room) to avoid redundant calls.
- Respect the system's battery optimization mode — check
PowerManager.isPowerSaveMode()and reduce background work accordingly. - Use WorkManager constraints (
requiresCharging,requiresNetworkType) to schedule expensive work intelligently.
Key Tools
- Android Studio Profiler — CPU, memory, network, and energy in one place.
- Perfetto — detailed system-level traces.
- Macrobenchmark — reliable startup and scroll benchmarks.
- LeakCanary — automatic memory leak detection in debug builds.