跪拜 Guibai
← All articles
Android · Android Jetpack

The Road to State-Driven Android: From MVC to MVI-Flavored MVVM

By 小巫debug日记 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Android teams get a clear, up-to-date decision map: what to adopt for new Compose projects, how to migrate legacy MVP code, and which official tools (StateFlow, Navigation 3, Hilt) align with Google's reference architecture. The convergence on a single UiState and UDF also means the platform's ecosystem now shares a common state-management vocabulary, reducing cross-project friction and making hiring and knowledge transfer more predictable.

Summary

This survey of Android architecture from 2008 to 2026 maps the progression from MVC's massive Activity through MVP's interface explosion to MVVM's data-driven views, and finally to the current mainstream: MVVM fused with MVI's single-state and explicit-intent ideas, coordinated under Google's official Unidirectional Data Flow (UDF) guidance. The official recommendation is not a specific pattern but a layered structure — UI, optional domain, and data layers with a Repository as single source of truth — plus UDF with state flowing down and events flowing up.

Practical comparisons cover nine dimensions across MVC, MVP, and MVVM, including testability, coupling, memory leak risk, and configuration change support. State holders like LiveData, StateFlow, and SharedFlow are examined, with StateFlow recommended for UI state and SharedFlow explicitly discouraged for one-off events. The article also details Compose's state hoisting, Navigation 3's state-driven back stack, the MVI/MVVM fusion, Clean Architecture's relation to the official layering, and the UseCase boilerplate debate, concluding with a five-step migration path from MVP to MVVM and a list of anti-patterns such as holding Activity context in ViewModel or scattering page state across multiple LiveData streams.

Takeaways
— Google does not mandate MVC, MVP, or MVVM; it recommends layered architecture plus unidirectional data flow, with separation of concerns and predictable state.
— The current industry mainstream is MVVM augmented with a single UiState, absorbing MVI's single-state-object, explicit-intent, and pure-reducer ideas.
— The recommended modern stack is Jetpack Compose for UI, StateFlow for state, Hilt for dependency injection, and Repository for data access.
— Navigation 3 makes the back stack a developer-managed observable state list, replacing imperative navigation with type-safe, state-driven routes.
— MVC in Android turns Activity into a Massive View Controller with poor testability and fragile state handling across configuration changes.
— MVP separates view and logic through Contract interfaces enabling pure JVM tests, but suffers from interface explosion and requires manual cancellation in detachView to avoid leaks.
— MVVM's ViewModel survives configuration changes and does not hold views, but one-off events like Toast or navigation need careful modeling, preferably as UI state.
— UDF's core is state flowing down and events flowing up, with the data layer feeding ViewModel and ViewModel exposing UI State.
— StateFlow requires an initial value and deduplicates emissions, making it the official choice for UI state; SharedFlow lacks an initial value and is not recommended for one-off UI events.
— The official architecture treats the domain layer as optional; UseCase classes should be introduced only for complex logic or reuse, not for simple forwarding.
— A five-step MVP-to-MVVM migration: introduce ViewModel, define a UiState, expose via StateFlow, remove Contract classes, and introduce a Repository, one page or module at a time.
— Anti-patterns include ViewModel holding Activity context, UI directly accessing Room/Retrofit, using SharedFlow for one-off events, misusing GlobalScope, and exposing the same page via multiple LiveData/StateFlow streams.
Conclusions

The line between MVI and MVVM has blurred so far that the article itself concedes the official UDF + StateFlow recommendation is a community-labeled variant of MVI; teams should stop debating labels and focus on single-state and unidirectional flow mechanics.

Navigation being pulled into state management, as Navigation 3 does, completes the declarative loop: once every UI concern including navigation is a value, testing and debugging become uniformly state-based.

The main cost of the modern stack is not the architecture itself but the reactive-programming learning curve; testing strategy shifts from mocking views to asserting state transitions.

The repeated warning against SharedFlow for one-off events suggests that even with official guidance, event modeling remains the least-standardized corner of Android architecture.

Concepts & terms
UDF (Unidirectional Data Flow)
An architectural principle where state flows down from a single source (typically a ViewModel) to the UI, and events flow up from the UI to that source, keeping state changes predictable and traceable.
SSOT (Single Source of Truth)
A design principle that a piece of data should exist in exactly one authoritative place, usually a Repository in Android, so that all parts of the app read from and write to that one source, avoiding inconsistent state.
MVI (Model-View-Intent)
An architecture pattern where the UI state is a single immutable data class, user actions are explicit intents, and a pure reducer function computes the new state from the current state and an intent, making state transitions deterministic.
State Hoisting
In Compose, moving a state variable to the lowest common ancestor of all composables that need it, passing it down as a parameter and events up as callbacks, aligning with UDF.
Navigation 3
Jetpack's upcoming navigation library that treats the back stack as a developer-owned observable list of typed Kotlin routes, making navigation a state change rendered by Compose rather than an imperative command.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗