Demystifying Android MVI: Origins, Misconceptions, and Best Practices
This article traces the evolution of Android's Model‑View‑Intent (MVI) architecture from its MVC and Flux roots, clarifies common misconceptions versus Redux and MVVM, and provides concrete Kotlin code examples and guidelines for implementing a truly unidirectional, immutable‑state MVI pattern in mobile apps.
1. Introduction: Discarding Stereotypes
MVI is an indispensable architecture pattern in Android development, offering clear and predictable data flow for stable UI, yet it is often misunderstood as a rigid, single‑style approach similar to Redux.
MVI adapts flexibly to Android, preserving its core ideas while improving usability.
This article explores MVI’s origins, the problems it solves, and why embracing its adaptability matters.
2. Architectural Precursors: Patterns that Gave Birth to MVI
MVI did not appear out of thin air; it evolved from earlier patterns such as MVC and reactive programming.
1979: Model‑View‑Controller (MVC)
MVC splits an application into three components:
Model : manages data and business logic
View : handles user input
Controller : processes input and drives the Model
May 2014: Flux
Facebook’s Flux introduced a unidirectional data flow consisting of Actions, Dispatcher, Store, and Views.
Actions : describe events
Dispatcher : distributes actions to the Store
Store : holds application state and updates in response to actions
Views : listen to Store changes and trigger rendering
Flux’s key innovation is the “single, circular data flow,” which became the core idea of MVI.
December 2014: MVI (Reactive MVC)
André Staltz re‑examined MVC from a reactive perspective, coining “Reactive MVC.” MVI borrowed Flux’s unidirectional flow, removing the Dispatcher and letting Intent drive the Model, forming a pure reactive transformation.
Intent : replaces Controller, turning user interactions into an event stream
Model : reacts to the stream and updates state
View : listens to state changes and renders UI
The resulting pattern is called Model‑View‑Intent (MVI), inheriting MVC’s one‑way flow and adapting it to observable streams.
3. The Origin of MVI
MVI was first proposed by André Staltz in 2014 for the Cycle.js framework and only later adopted by Android after Redux became popular.
MVI Practical Definition
MVI enforces a “single, immutable state” and a “unidirectional event flow.” Core components are Model (holds state), View (renders state), and Intent (event stream from UI).
References such as Staltz’s “Unidirectional User Interface Architectures” describe these components in detail.
Code Example
Jannis Dornemann expressed MVI’s core idea with a mathematical expression, which can be implemented as follows:
// Intent is the user interaction event stream
val intents: Flow<Intent> = view.userInteractions()
// Model processes Intent and produces a state stream
val states: Flow<State> = intents.map { intent ->
model.reduce(intent)
}
// View collects the state stream and renders UI
states.collect { state ->
view.render(state)
}This code directly reflects the mathematical model.
4. Misconceptions: What MVI Is Not
A common mistake is equating MVI with Redux, assuming it must use a global Store and Reducer.
Misconception 1: MVI = Redux
While MVI inspired Redux’s unidirectional flow, Redux is a centralized global state manager, whereas MVI typically uses distributed state per module.
Key differences between Redux and MVI:
State Management : Redux uses centralized global state; MVI uses distributed per‑module state.
Reducer : Redux has a single global reducer; MVI has an independent reducer for each module.
Data Flow : Redux follows dispatch(Action) → Store → Reducer → State; MVI follows Intent → Model → New State → View.
Control Logic : Redux relies on an external Dispatcher; MVI handles Intent internally within the module.
Thus Android MVI does not have to follow Redux’s exact pattern.
Misconception 2: MVI = MVVM
Both use observable streams and immutable state, but MVVM features bidirectional communication between View and ViewModel, whereas MVI enforces a one‑way flow: Intent → Model → State → View.
Key differences between MVVM and MVI:
State : MVVM distributes state across multiple LiveData/StateFlow objects; MVI keeps a single immutable state per module.
Data Flow : MVVM is bidirectional (View ↔ ViewModel ↔ Model); MVI is strictly unidirectional (Intent → Model → State → View).
Event Handling : MVVM processes events scattered throughout the ViewModel; MVI centralizes them as an Intent stream.
Reducer : MVVM does not require a reducer; MVI mandates a reducer to generate new state.
The key difference lies in whether the View holds state.
5. How to Write a Proper MVI Implementation
Core characteristics:
Single immutable state model (e.g., ViewState)
State managed in Model
Intent abstracts user actions
Intent triggers state transitions via a reducer
Strict unidirectional flow: Intent → State → View
Required Data Models
UiState : immutable UI‑focused state
Intent : user‑initiated actions
SideEffect : one‑off events such as logging or navigation
// UI State data class (immutable)
data class UserUiState(
val username: String = "",
val age: Int = 0,
val isLoading: Boolean = false
)
// User intents
sealed interface UserIntent {
data class ChangeUsername(val newName: String) : UserIntent
object IncrementAge : UserIntent
object DecrementAge : UserIntent
}
// Side effects
sealed interface UserSideEffect {
data class ShowMessage(val message: String) : UserSideEffect
}ViewModel Responsibilities
The ViewModel should expose an immutable StateFlow of the UI state, process Intents, and emit SideEffects.
class UserViewModel : ViewModel() {
private val _sideEffects = MutableSharedFlow<UserSideEffect>()
val sideEffects = _sideEffects.asSharedFlow()
private val _uiState = MutableStateFlow(UserUiState())
val uiState = _uiState.asStateFlow()
fun processIntent(intent: UserIntent) {
when (intent) {
is UserIntent.ChangeUsername -> _uiState.update { it.copy(username = intent.newName) }
UserIntent.IncrementAge -> {
_uiState.update { it.copy(age = it.age + 1) }
viewModelScope.launch {
_sideEffects.emit(UserSideEffect.ShowMessage("Age increased!"))
}
}
UserIntent.DecrementAge -> {
if (uiState.value.age > 0) {
_uiState.update { it.copy(age = it.age - 1) }
}
}
}
}
}Why MVI Is Hard to Standardize
MVI is an architectural mindset rather than a fixed template; teams can adapt it to their needs while preserving the core principles of unidirectional flow, immutable state, and Intent‑driven updates.
6. Final Thoughts: Choosing the Right Architecture
Adopt MVI only when its predictability outweighs the overhead for your team.
Do not use MVI for its own sake; evaluate the problem first.
MVI challenges developers to think about state changes and event flow, leading to cleaner code.
A global Redux‑style store is optional; lightweight per‑module implementations are sufficient.
If the architecture follows the core MVI principles, it embodies the spirit of MVI regardless of strict naming.
References
André Staltz, “Architectural patterns for interactive programs,” https://staltz.com/some‑problems‑with‑react‑redux
André Staltz, “Nothing new in React and Flux except one thing,” https://staltz.com/nothing‑new‑in‑react‑and‑flux‑except‑one‑thing
André Staltz, “Unidirectional User Interface Architectures,” https://staltz.com/unidirectional‑user‑interface‑architectures
Futurice, “Reactive MVC and the Virtual DOM,” https://www.futurice.com/blog/reactive‑mvc‑and‑the‑virtual‑dom
Redux history, https://redux.js.org/understanding/history‑and‑design/history‑of‑redux#2015‑the‑birth‑of‑redux
Recommended Reading
谈谈在实际项目中使用 MVI 后的感悟 – https://mp.weixin.qq.com/s?__biz=Mzg5MzYxNTI5Mg==∣=2247494446&idx=1&sn=dc6036d371e4faca7ea8d504f621a630&scene=21#wechat_redirect
从源头了解 MVI 的核心思想 – https://mp.weixin.qq.com/s?__biz=Mzg5MzYxNTI5Mg==∣=2247492860&idx=1&sn=2cf20dcb89ed532c8bbb3721f987a59f&scene=21#wechat_redirect
MVVM 成为历史,Google 全面倒向 MVI – https://mp.weixin.qq.com/s?__biz=Mzg5MzYxNTI5Mg==∣=2247489834&idx=1&sn=4540f0e74d10878ec2675ec068ce7763&scene=21#wechat_redirect
AndroidPub
Senior Android Developer & Interviewer, regularly sharing original tech articles, learning resources, and practical interview guides. Welcome to follow and contribute!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.