A Simple Android Application that demonstrates MVI architecture.
June 1, 2024
A simple Android application demonstrating the MVI (Model-View-Intent) architecture. This application will include a basic counter functionality where the user can increment or decrement a counter value.
Project Structure
- Model
- View
- Intent
- ViewState
Dependencies
Make sure you have the following dependencies in your build.gradle
file:
dependencies {
implementation “androidx.appcompat:appcompat:1.2.0”
implementation “androidx.lifecycle:lifecycle-livedata-ktx:2.2.0”
implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0”
implementation “org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9”
implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9”
}
1. Model
CounterModel.kt
data class CounterState(val count: Int = 0)
sealed class CounterIntent {
object Increment : CounterIntent()
object Decrement : CounterIntent()
}
2. View
activity_main.xml
<TextView
android:id="@+id/counterTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="32sp"
android:layout_marginBottom="16dp"/>
<Button
android:id="@+id/incrementButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Increment"
android:layout_marginBottom="8dp"/>
<Button
android:id="@+id/decrementButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Decrement"/>
3. ViewModel
CounterViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
class CounterViewModel : ViewModel() {
private val initialState = CounterState()
private val _state = MutableStateFlow(initialState)
val state: StateFlow<CounterState> get() = _state
private val _intentChannel = Channel<CounterIntent>(Channel.UNLIMITED)
init {
handleIntents()
}
fun sendIntent(intent: CounterIntent) {
viewModelScope.launch {
_intentChannel.send(intent)
}
}
private fun handleIntents() {
viewModelScope.launch {
_intentChannel.consumeAsFlow().collect { intent ->
when (intent) {
is CounterIntent.Increment -> {
val newState = _state.value.copy(count = _state.value.count + 1)
_state.value = newState
}
is CounterIntent.Decrement -> {
val newState = _state.value.copy(count = _state.value.count - 1)
_state.value = newState
}
}
}
}
}
}
4. Activity
MainActivity.kt
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private val viewModel: CounterViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
viewModel.state.collect { state ->
counterTextView.text = state.count.toString()
}
}
incrementButton.setOnClickListener {
viewModel.sendIntent(CounterIntent.Increment)
}
decrementButton.setOnClickListener {
viewModel.sendIntent(CounterIntent.Decrement)
}
}
}
Explanation
- Model: The
CounterState
represents the state of the counter. TheCounterIntent
represents the possible actions (increment and decrement). - View: The
activity_main.xml
file defines the UI with aTextView
to display the counter and two buttons to increment and decrement the counter. - ViewModel: The
CounterViewModel
manages the state and handles intents. It uses aChannel
to receive intents and aStateFlow
to emit the current state. - Activity: The
MainActivity
observes theStateFlow
from theViewModel
and updates the UI accordingly. It also sends intents to theViewModel
when buttons are clicked.
This code provides a basic implementation of the MVI architecture, demonstrating how to handle state and user interactions in a clean and maintainable way.
Thanks!!