Jetpack Compose

Android's modern declarative UI toolkit. 60% of top 1,000 Play Store apps now use Compose.

Key Principles

  • State Hoisting - Lift state to lowest common ancestor. Composables should be stateless.
  • Unidirectional Data Flow - State down, events up via callbacks.
  • Three-Phase Rendering - Composition (what) > Layout (where) > Drawing (how). Skip phases with lambda modifiers.
  • Type Stability - Use @Immutable/@Stable annotations for smart recomposition skipping.

Essential Patterns

// State hoisting + lifecycle-aware collection
@Composable
fun LoginScreen(viewModel: LoginViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    LoginContent(
        email = uiState.email,
        onEmailChange = viewModel::updateEmail,
        onLogin = viewModel::login
    )
}

// Deferred state reads for performance
@Composable
fun Title(scrollProvider: () -> Int) {
    Column(modifier = Modifier.offset {
        IntOffset(x = 0, y = scrollProvider()) // Layout phase only
    }) { /* ... */ }
}

2025 Features

  • Pausable Composition - Split work across frames, eliminating jank
  • Background Text Prefetch - Pre-warm text layout caches on background threads

Material Design 3

Dynamic color, tonal elevation, and M3 Expressive updates (MDC-Android 1.14.0+).

  • Use dynamicDarkColorScheme() / dynamicLightColorScheme() on Android 12+
  • Always provide fallback color schemes for pre-Android 12
  • Adaptive navigation: NavigationBar (compact) / NavigationRail (medium) / PermanentDrawer (expanded)

Animations

  • AnimatedVisibility - Show/hide with enter/exit transitions
  • animateContentSize() - Smooth size changes
  • AnimatedContent - State-based content transitions
  • spring() - Physics-based natural motion (preferred over tween)
  • Use Modifier.graphicsLayer{} for GPU-accelerated draw-phase-only animations

MVVM + Clean Architecture

Three-layer separation: UI (Compose) > Domain (Use Cases) > Data (Repository + DataSource).

// Sealed class for UI state
sealed interface ProfileUiState {
    data object Loading : ProfileUiState
    data class Success(val profile: UserProfile) : ProfileUiState
    data class Error(val message: String) : ProfileUiState
}

// ViewModel with StateFlow
class ProfileViewModel(
    private val getProfile: GetUserProfileUseCase,
    savedStateHandle: SavedStateHandle
) : ViewModel() {
    private val userId = savedStateHandle.get<String>("userId")!!

    val uiState: StateFlow<ProfileUiState> = getProfile(userId)
        .map { ProfileUiState.Success(it) }
        .catch { emit(ProfileUiState.Error(it.message ?: "Unknown error")) }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ProfileUiState.Loading)
}

Coroutines & Flow

  • StateFlow - Hot flow for UI state (always has value, conflated)
  • SharedFlow - Hot flow for events (no initial value, configurable replay)
  • collectAsStateWithLifecycle() - Lifecycle-aware collection in Compose
  • stateIn() - Convert cold Flow to StateFlow with WhileSubscribed strategy
  • Use viewModelScope for ViewModel coroutines, lifecycleScope for UI

Hilt Dependency Injection

  • @HiltAndroidApp on Application class
  • @AndroidEntryPoint on Activities/Fragments
  • @HiltViewModel + @Inject constructor for ViewModels
  • @Binds for interface bindings, @Provides for third-party objects
  • Scopes: @Singleton, @ViewModelScoped, @ActivityScoped

Performance Optimization

Priority Checklist
1) Baseline Profiles (30-50% startup improvement), 2) R8 Full Mode, 3) Lazy initialization, 4) Image optimization with Coil/Glide, 5) LazyColumn with stable keys.
  • Baseline Profiles - Pre-compile critical user journeys. Google Maps: 30% faster startup.
  • R8 Full Mode - Maximum code shrinking. Disney+: 30% faster startup, 25% fewer ANRs.
  • App Startup Library - Manage initialization order and defer non-critical init
  • Target under 500ms TTID (Time to Initial Display)
  • LeakCanary - Always add to debug builds for memory leak detection

Data & Security

  • Room - SQLite with type-safe queries, Flow support, migrations
  • Retrofit + OkHttp - HTTP networking with interceptors and caching
  • Security - Use Tink for encryption (EncryptedSharedPreferences deprecated), Android Keystore for keys
  • WorkManager - Guaranteed background work with constraints
  • Offline-First - Room as single source of truth, sync with network
  • Paging 3 - Efficient large dataset loading with PagingSource and RemoteMediator

Testing

Testing pyramid: 70% unit, 20% integration, 10% UI tests.

  • MockK - Kotlin-first mocking with coroutine support
  • Turbine - Flow testing made simple
  • Compose Test - Semantic-based UI testing with createComposeRule
  • Robolectric - Fast Android tests without emulator
  • Paparazzi/Roborazzi - Screenshot testing for UI regression