Chapter 7

Platform & Performance

Background Work · App Startup · Memory Leaks · Battery · ANR · Performance Profiling

Ready to practise interactively?

Explore this chapter with quizzes, diagrams, and real-world examples in the full interactive experience.

Open Interactive Chapter →

App Startup Optimization

App startup is a top interview topic at Google. Know all three start types and the modern tools.

  • Baseline Profiles — pre-compiles critical code paths at install time. Reduces cold start 20–40% on average, typically toward the lower end for most apps. Generated with Macrobenchmark. Baseline Profiles are a critical L6-level performance optimization. Performance optimization is heavily assessed at L6+, and Baseline Profiles represent the current state of the art.
  • App Startup library — replaces ContentProvider-based initialization. Declare initializers, control startup order, enable lazy init.
  • Lazy initialization — defer non-critical SDKs (analytics, crash reporting) until after first frame
  • Main thread work — profile Application.onCreate() with Android Profiler; move any I/O or heavy computation off main thread immediately
  • Splash Screen API — use SplashScreen API (API 31+) for smooth startup animation; avoid custom splash Activities
Start TypeWhat HappensOptimization Target
Cold startProcess created, Application.onCreate(), first Activity. Slowest.< 2 seconds. Biggest impact opportunity.
Warm startProcess alive, Activity recreated from back stack.< 1 second.
Hot startApp in memory, Activity brought to foreground.< 500ms. Usually fast already.

Recommended Libraries

  • App Startup Jetpack library for initializing components at app startup. Replaces ContentProvider chains. Controls init order and supports lazy initialization.
  • ProfileInstaller Installs Baseline Profiles at app install time. Required to get cold start benefits from Baseline Profiles on pre-Android 9 devices.

Memory Management & Leaks

Memory leaks are a classic L6 question. Know causes, detection, and fixes.

  • LeakCanary — add to debug builds only; automatically detects and reports Activity/Fragment/ViewModel leaks
  • Memory Profiler — in Android Studio; take heap dumps; look for Activity instances that should be GC'd
Leak CauseExampleFix
Context leak via static referencestatic Activity reference in a singletonUse Application context for singletons; never store Activity in static field
Anonymous inner class / lambdaHandler(Looper.main) { } inside Activity — holds implicit referenceUse WeakReference or move to ViewModel
Unregistered listenerRegistering BroadcastReceiver in onCreate, never unregisteringUnregister in onDestroy or use lifecycle-aware components
ViewModel holding View referencePassing a View or Activity into ViewModel constructorViewModels must never hold View/Activity refs. Use Application context if needed.
Coroutine not cancelledGlobalScope.launch { } inside Activity — outlives ActivityUse lifecycleScope or viewModelScope exclusively
Bitmap not recycledHolding large Bitmap in memory past its useUse image loading libraries (Coil/Glide) that manage bitmap recycling automatically

Recommended Libraries

  • LeakCanary Automatic memory leak detection for Android debug builds. Detects Activity, Fragment, ViewModel, and custom object leaks. Zero config.

Background Processing

Know when to use each background processing API.

  • WorkManager constraints — setRequiresNetworkType(CONNECTED), setRequiresBatteryNotLow(true), setRequiresCharging(true)
  • Chained work — WorkManager.beginWith(a).then(b).then(c).enqueue() — compress → upload → notify
  • Doze mode — Android restricts background work during Doze. WorkManager is Doze-safe. FCM high-priority messages bypass Doze.
APIUse WhenGuaranteed?
WorkManagerDeferrable, guaranteed background work (sync, uploads, analytics)Yes — survives process death and reboots
Foreground ServiceUser-visible long-running work (media, navigation, download)Yes — shown in notification tray
Coroutine (lifecycleScope)Short background work within a lifecycleNo — cancelled with lifecycle
AlarmManagerExact-time alarms (calendar reminders)Yes — but not for network work

Recommended Libraries

  • WorkManager Jetpack's background work scheduler. Deferrable, guaranteed, survives reboots. Standard for Android background tasks.
  • WorkManager KTX Kotlin extensions for WorkManager. CoroutineWorker for suspend functions.

ANR — App Not Responding

ANR is triggered when the main thread is blocked. Know the specific timeouts.

  • StrictMode — enable in debug builds to catch accidental main thread I/O before it causes ANRs
  • Avoid — SharedPreferences.apply() (async) is safe; .commit() (sync) on main thread causes ANRs
ANR TypeTimeoutCommon Cause
Input dispatch timeout5 secondsBlocking main thread during touch/key event handling
Broadcast receiver timeout10 seconds (foreground), 60s (background)Doing I/O in BroadcastReceiver.onReceive()
Service start timeout20 seconds (foreground), 200s (background)Long-running work in Service.onCreate()
Content provider timeout10 secondsSlow query in ContentProvider

Battery & Network Efficiency

Mobile apps must be good citizens regarding battery and network usage.

  • App Standby Buckets — Android limits background work based on app usage. Active > Working Set > Frequent > Rare > Restricted.
  • Battery Historian — analyse battery drain; identify wakelock abuse and unnecessary wake-ups
  • Wakelocks — avoid partial wakelocks; use WorkManager instead. Never hold a wakelock indefinitely.
  • Network scheduling — batch non-urgent requests. Use WiFi-only constraint for large uploads.
  • Adaptive polling — increase polling interval under low battery or poor connectivity
  • Push over pull — FCM push is far more battery-efficient than polling every N seconds

WorkManager + Outbox + Room — End-to-End

How background sync, local persistence, and guaranteed delivery fit together as one system.

  • Worker types — CoroutineWorker (use this, suspend doWork()); ListenableWorker (legacy Java/callback interop); avoid plain Worker for new code
  • Input/output data — pass via workDataOf(); limited to 10KB. For larger data, pass a Room row ID, not the object itself.
  • Result.retry() — triggers backoff. Set BackoffPolicy.EXPONENTIAL on the WorkRequest. Implement your own retry counter in Room — WorkManager has no built-in max retry.
  • ExistingWorkPolicy.KEEP — prevents duplicate workers when user action triggers sync multiple times before the first worker finishes
Outbox Entry StatusMeaningAction
PENDINGWritten locally, not sent yetWorkManager will pick up and send
IN_FLIGHTWorker is currently sendingSkip — do not retry to avoid duplicates
SENTServer confirmed, server ID assignedSafe to clean up after TTL
FAILEDExceeded max retriesSurface error in UI, allow manual retry
SeniorKnows WorkManager API
StaffDesigns the full outbox pattern with atomic Room transactions and idempotent workers
PrincipalDefines the sync reliability contract for the entire app — what can be lost vs what must survive

Streaming API Responses — SSE + Flow + Room

How to wire a streaming API response through the Android stack correctly.

  • OkHttp streaming — read response.body().source() line by line in a loop; each SSE event is a data: line
  • callbackFlow { } — wraps the OkHttp callback into a Kotlin Flow; use trySend() for non-blocking emit; awaitClose { call.cancel() } for cleanup
  • ViewModel collects the Flow — updates StateFlow on each token; UI recomposes incrementally showing tokens as they arrive
  • Persist on completion — buffer tokens in memory during streaming; write the full assembled response to Room only when stream ends. Room Flow then re-emits history list automatically.
  • Error mid-stream — catch IOException in Flow.catch { }; update UiState with partial response + error indicator; allow user to retry from last position if API supports cursor resumption
StrategyWhen to Use
Buffer in memory, write on completionDefault for chat/LLM. Simplest. Risk: crash mid-stream loses response.
Write chunks to Room as they arriveLong document/code generation where interruption is costly. Resumable.
Stream to UI only, never persistEphemeral data (live prices, scores). No history needed.

Recommended Libraries

  • okhttp-eventsource SSE client by LaunchDarkly built on OkHttp. Production-grade, auto-reconnect, backoff.
  • Ktor SSE Kotlin-first SSE client with coroutines. Part of Ktor framework. Good for KMP.

Video Streaming — ExoPlayer / Media3

Netflix, YouTube, TikTok roles will ask about this. Know the architecture, not just the API.

  • Media3 / ExoPlayer — the standard Android media player. Supports HLS, DASH, SmoothStreaming, progressive MP4.
  • Adaptive bitrate (ABR) — HLS/DASH splits video into segments at multiple quality levels. Player monitors bandwidth and switches quality per segment. No visible pause.
  • Buffering strategy — configure minBufferMs, maxBufferMs, bufferForPlaybackMs. Longer buffer = smoother playback but more memory and data usage.
  • Background playback — requires a Foreground Service + MediaSession. Media3 MediaSessionService handles this with minimal boilerplate.
  • Offline download — DownloadManager in Media3 segments and caches video chunks to disk. Requires a Download Service (ForegroundService).
  • DRM (Widevine) — ExoPlayer handles L1/L3 Widevine via DefaultDrmSessionManager. L1 = hardware-backed (device-level capability). Devices with L1 support can stream Netflix HD+ (1080p+); L3-only devices are capped at SD (480p). Apps use ExoPlayer's DRM integration — the device's Widevine level determines available quality.

Recommended Libraries

  • Media3 (ExoPlayer) Jetpack's official media player. Successor to standalone ExoPlayer. Supports HLS, DASH, DRM, background playback.
  • media3-exoplayer Core ExoPlayer module in Media3. Start here for basic playback.
  • media3-ui Pre-built player UI components (PlayerView). Customizable controls, picture-in-picture support.
  • media3-session MediaSession integration for background playback, notification controls, and Android Auto.
SeniorIntegrates ExoPlayer
StaffTunes buffering strategy, handles ABR quality switching, implements background playback correctly
PrincipalDesigns the full media pipeline including CDN, pre-warming, and download management

File Upload & Download Manager

Asked at Google (Drive), Dropbox, any media-heavy app. The key is resumability.

  • Chunked upload — split file into chunks (e.g. 5MB each); upload sequentially; track which chunks succeeded in Room. On failure, resume from last successful chunk.
  • HTTP Range requests — for resumable downloads, server must support Range header. Client sends Range: bytes=X- to resume from byte X after interruption.
  • Progress tracking — emit progress via Flow from the Worker; ViewModel observes WorkInfo.progress for WorkManager-based downloads
  • Pause / Resume — store byte offset in Room; cancel the Worker; on resume, create a new Worker that reads the offset and sends the Range header
  • Storage management — check available space before starting; use getExternalFilesDir() or MediaStore for downloads; clean up incomplete chunks on cancel
  • Foreground Service — large downloads need a ForegroundService with a notification showing progress so Android doesn't kill the process

Performance Regression Prevention in CI

Shipping a performance regression is easy. Finding it 3 sprints later is expensive. The modern approach: measure in CI, fail the build on regressions, and block the PR before it merges.

  • Macrobenchmark in CI — add a :benchmark module; run on a physical device or Firebase Test Lab; FrameTimingMetric catches jank regressions
  • StartupTimingMetric — measures TTID (Time to Initial Display) and TTFD (Time to Full Display) for cold/warm/hot starts
  • FrameTimingMetric — counts frames >16ms (jank); catches scroll performance regressions in RecyclerView or LazyColumn
  • Benchmarks require a profileable build variant — add android { buildTypes { benchmark { ... } } } to your build.gradle
  • Firebase Test Lab — run benchmarks on real devices in CI; Robo test can catch startup crashes; custom Instrumentation runs Macrobenchmarks
  • Fail threshold — set a baseline JSON file; if p50 startup increases by >10%, fail the CI step

Recommended Libraries

  • Macrobenchmark Jetpack library for measuring startup, scroll jank, and animation in CI. FrameTimingMetric, StartupTimingMetric. Run on real devices.
  • Firebase Test Lab Cloud device farm for running automated tests and benchmarks on real Android devices. Integrates with CI pipelines.
SeniorProfiles manually with Android Studio Profiler
StaffAdds Macrobenchmark to CI; defines pass/fail thresholds for startup and scroll performance
PrincipalOwns the performance budget across the org; gates releases on FrameTimingMetric and StartupTimingMetric baselines

Interview tip: When discussing performance tooling, mention the full chain: Recomposition Highlighter (dev) → Perfetto trace (profiling) → Macrobenchmark (CI prevention) → Baseline Profiles (shipped optimisation). This shows you think in systems, not one-off fixes.

Real-Time Location Tracking

Uber, DoorDash, Lyft core topic. Three distinct problems: getting location, sending it, doing it efficiently.

  • FusedLocationProviderClient — always use this, never raw GPS/Network. Google Play Services fuses GPS, WiFi, cell to give best accuracy at lowest battery cost.
  • Background location (Android 10+) — requires ACCESS_BACKGROUND_LOCATION permission. Must run inside a Foreground Service with location-type notification.
  • Location accuracy tiers — PRIORITY_HIGH_ACCURACY (GPS, drains battery); PRIORITY_BALANCED_POWER (WiFi/cell, good for most); PRIORITY_LOW_POWER (city-level, minimal battery)
  • Sending updates to server — buffer location updates locally; send in batches via WebSocket or HTTP POST every N seconds to reduce network overhead
  • Geofencing — use Geofencing API for enter/exit triggers; far more battery efficient than polling location against a boundary
ScenarioStrategy
Active ride tracking (Uber driver)Foreground Service + HIGH_ACCURACY + WebSocket send every 2–5s
Passive location (delivery ETA)Background BALANCED_POWER + batch HTTP send every 30s
Geofence trigger (arrive at store)Geofencing API — zero battery when outside fence radius

Recommended Libraries

  • FusedLocationProviderClient Google Play Services location API. Fuses GPS, WiFi, cell for best accuracy at lowest battery. Always use this over raw GPS.
  • play-services-location The dependency for FusedLocationProvider. Required for any location work on Android.

Test your knowledge

This chapter includes 7 quiz questions covering all core concepts. Open the interactive experience to test yourself.

Start Quiz →