This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

preface

Android development to today has been quite mature, a variety of architecture we are also familiar with, such as MVC,MVP,MVVM, etc., among which MVVM is officially recommended, become prominent in Android development. However, there is no silver bullet in software development, MVVM architecture is not perfect, there will be some inconvenient in the use process, and MVI can be a good solution to part of the PAIN points of MVVM. This article mainly includes the following contents

  1. MVC.MVP.MVVMAnd so on classical architecture
  2. MVIWhat exactly is architecture?
  3. MVIArchitecture of actual combat

It should be pointed out that the title says that MVI architecture is an advanced version of MVVM, which means that MVI is very similar to MVVM and has made some improvements on its basis. It does not mean that MVI architecture is necessarily more suitable for your project than MVVM. Students can choose the architecture suitable for the project scenario after analyzing and comparing various architectures

Introduction to classic Architecture

MVCArchitecture is introduced

MVCIt’s an ancientAndroidDevelop the architecture along withMVPwithMVVMThe popularity of… has gradually faded from the historical stage. We make a simple introduction here, and its architecture is shown as follows:



MVCThe architecture is divided into the following parts

  1. The view layer (View) : correspond toxmlLayout files andjavaCode dynamicviewPart of the
  2. Control layer (Controller) : Mainly responsible for business logic, inandroidbyActivityBear, at the same time becauseXMLThe view is too weak, soActivityTaking on too many functions to display the view and add control logic.
  3. The model layer (Model) : Mainly responsible for network request, database processing,I/OThat is, the data source of the page

Due to theandroidIn thexmlThe layout is too weak for functionality,ActivityIn fact in charge ofViewLayer andControllerLayer both work, so inandroidIn themvcIt’s more like this:



soMVCArchitecture inandroidThe main problems on the platform are as follows:

  1. ActivityAlso responsible forViewwithControllerLayer of work that violates the principle of single responsibility
  2. ModelLayer andViewThere is coupling and interdependence between layers, which violates the principle of minimum knowledge

MVPArchitecture is introduced

Due to theMVCArchitecture inAndroidSome of the flaws in the platform,MVPThe resulting architecture is shown below



MVPThe architecture is divided into the following parts

  1. ViewLayer: corresponds toActivitywithXMLIs only responsible for displayUI, only withPresenterLayer interaction, andModelLayers are not coupled
  2. PresenterLayer: handles the business logic and calls back through the interfaceViewlayer
  3. ModelLayer: mainly responsible for network requests, database processing and other operations, there is no change in this

As we can see, MVP solves two problems with MVC, namely the Activity taking on two layers of responsibility and coupling the View layer to the Model layer

But the MVP structure has its own problems

  1. PresenterLayers interact with each other through interfacesViewCommunication actually holds upViewA reference to the
  2. But as the business logic grows, a page can become so complex that it can causeViewThe interface will be huge.

MVVMArchitecture is introduced

MVVMModel willPresenterrenamedViewModel, basically withMVPThe pattern is exactly the same.

The only difference is that it uses two-way data binding (data-binding) :ViewChanges are automatically reflected inViewModelAnd vice versa

MVVMThe architecture diagram is as follows:



It can be seen thatMVVMwithMVPThe main difference is that you don’t have to actively refreshUIAs long asModelWhen the data changes, it automatically shows upUIOn. In other words,MVVMIt’s more automatedMVP.

MVVMThe bidirectional data binding is mainly throughDataBindingImplementation, but I believe there are many people like me, is not like to useDataBindingSo the architecture becomes the following

  1. ViewTo observe theViewModleThe data changes and updates itself. This is actually a single data source and not a two-way data binding, so in factMVVM“Is a feature I don’t actually use
  2. ViewBy calling theViewModelProvides methods to come withViewMdoelinteraction

summary

  1. MVCThe main problem with architecture is thatActivityTo undertake theViewwithControllerTwo layers of responsibility, at the same timeViewLayer andModelThere is coupling in layers
  2. MVPThe introduction ofPresenterLayer to solve theMVCTwo problems with architecture,ViewOnly withPresenterLayer interaction, business logic inPresenterlayer
  3. MVPThe problem is that as business logic increases,ViewThe interface will be huge,MVVMThe architecture solves this problem with two-way data binding
  4. MVVMwithMVPThe main difference is that you don’t have to actively refreshUIAs long asModelWhen the data changes, it automatically shows upUIOn. In other words,MVVMIt’s more automatedMVP.
  5. MVVMThe bidirectional data binding is mainly throughDataBindingYes, but a lot of people (like me) don’t like to use itDataBinding, butViewthroughLiveDataSuch as watchViewModleThis is a single data source rather than a two-way data binding

MVIWhat exactly is architecture?

MVVMWhat are the weaknesses of the architecture?

In order to ensure the one-way flow of data, LiveData must be converted to IMmutable when exposed. This requires a lot of template code and is easy to forget, as shown in the following

class TestViewModel : ViewModel() {
    // To ensure that exposed LiveData is immutable, adding a state requires adding two LiveData variables
    private val _pageState: MutableLiveData<PageState> = MutableLiveData()
    val pageState: LiveData<PageState> = _pageState
    private val _state1: MutableLiveData<String> = MutableLiveData()
    val state1: LiveData<String> = _state1
    private val _state2: MutableLiveData<String> = MutableLiveData()
    val state2: LiveData<String> = _state2
    / /...
}
Copy the code

As shown above, if the page logic is complex, the ViewModel will have many global variable LiveData, and each LiveData must be defined twice, one mutable and one immutable. This is actually the hardest part of writing complex pages using the MVVM architecture. Second is the View layer by calling the method of the ViewModel layer to interact, View layer and ViewModel interaction is more scattered, not systematic

To summarize, in my use, the MVVM architecture has the following major shortcomings

  1. To ensure exposure to the outside worldLiveDataIs immutable, requires a lot of template code to add and is easy to forget
  2. ViewLayer andViewModelLayer interactions are scattered and disorganized

MVIWhat is architecture?

MVIMVVMIt is similar in that it borrows from the ideas of the front-end framework, with more emphasis on one-way flow of data and unique data sources, as shown in the architecture diagram below



It is mainly divided into the following parts

  1. ModelAnd:MVVMIn theModelThe difference is,MVItheModelMainly refers toUIState (State). Page loading status, control location, etcUIstate
  2. View: with the otherMVXIn theViewConsistent, maybe oneActivityOr anyUICarrying unit.MVIIn theViewBy subscribing toIntentChanges to implement interface refresh (note: this is notActivitytheIntent)
  3. Intent: thisIntentnotActivitytheIntentAny operation of the user is wrapped asIntentThe coma toModelLayer to make data requests

Unidirectional data flow

MVI emphasizes the one-way flow of data, which is mainly divided into the following steps:

  1. User operation toIntentForm notice ofModel
  2. ModelBased on theIntentupdateState
  3. ViewTo receiveStateChanges refresh the UI.

Data always flows in one direction in a circular structure, not the other:

The above briefly introduced the MVI architecture, let’s see how to use the MVI architecture in detail

MVIArchitecture of actual combat

Overall architecture diagram



We use theViewModelTo carryMVItheModelLayer, the overall structure also withMVVMSimilar, but the main difference isModelwithViewPart of the layer interaction

  1. ModellayerUIState, and exposedViewStateforViewTo subscribe to,ViewStateIs adata classContains all page states
  2. ViewLayer throughActionupdateViewStateTo replaceMVVMBy calling theViewModelHow methods interact

MVIintroduces

addViewStatewithViewEvent

ViewState holds all the states of the page, and ViewEvent is a one-time event, such as Toast, as shown below

data class MainViewState(val fetchStatus: FetchStatus, val newsList: List<NewsItem>)  

sealed class MainViewEvent {
    data class ShowSnackbar(val message: String) : MainViewEvent()
    data class ShowToast(val message: String) : MainViewEvent()
}
Copy the code
  1. We are hereViewStateOnly two are defined, one is the request state and one is the page data
  2. ViewEventAlso very simple, a simple sealing class, displayToastwithSnackbar

ViewStateupdate

class MainViewModel : ViewModel() {
    private val _viewStates: MutableLiveData<MainViewState> = MutableLiveData()
    val viewStates = _viewStates.asLiveData()
    private val _viewEvents: SingleLiveEvent<MainViewEvent> = SingleLiveEvent()
    val viewEvents = _viewEvents.asLiveData()

    init {
        emit(MainViewState(fetchStatus = FetchStatus.NotFetched, newsList = emptyList()))
    }

    private fun fabClicked(a) {
        count++
        emit(MainViewEvent.ShowToast(message = "Fab clicked count $count"))}private fun emit(state: MainViewState?). {
        _viewStates.value = state
    }

    private fun emit(event: MainViewEvent?). {
        _viewEvents.value = event
    }
}
Copy the code

As shown above

  1. We just need to defineViewStatewithViewEventtwoState, and the subsequent increase of state indata classYou don’t need to write template code
  2. ViewEventsIt’s a one-time passSingleLiveEventImplementation, of course you can use itChannelWhen to achieve
  3. When the status is updated, passemitTo update the status

ViewListening to theViewState

    private fun initViewModel(a) {
        viewModel.viewStates.observe(this) {
            renderViewState(it)
        }
        viewModel.viewEvents.observe(this) {
            renderViewEvent(it)
        }
    }
Copy the code

As you can see above, MVI uses ViewState to centrally manage State. You only need to subscribe to a ViewState to get all the State of a page, which reduces a lot of template code compared to MVVM.

ViewthroughActionupdateState

class MainActivity : AppCompatActivity() {
	private fun initView(a) {
        fabStar.setOnClickListener {
            viewModel.dispatch(MainViewAction.FabClicked)
        }
    }
}
class MainViewModel : ViewModel() {
    fun dispatch(action: MainViewAction) =
        reduce(viewStates.value, action)

    private fun reduce(state: MainViewState? , viewAction:MainViewAction) {
        when (viewAction) {
            is MainViewAction.NewsItemClicked -> newsItemClicked(viewAction.newsItem)
            MainViewAction.FabClicked -> fabClicked()
            MainViewAction.OnSwipeRefresh -> fetchNews(state)
            MainViewAction.FetchNews -> fetchNews(state)
        }
    }
}
Copy the code

As shown above, the View interacts with the ViewModel through Action and communicates through Action, which is conducive to further decoupling between View and ViewModel. Meanwhile, all calls are summarized in the form of Action, which is also conducive to centralized analysis and monitoring of behavior

conclusion

This article mainly introduces MVC,MVP,MVVM and MVI architectures. Currently,MVVM is the official recommended architecture, but there are still several pain points

  1. MVVMwithMVPThe main difference is two-way data binding, which many people (like me) don’t like to useDataBindg“Is not actually usedMVVMBidirectional binding features, but a single data source
  2. When pages are complex, there is a lot to defineState, and need to define both mutable and immutable, the state will double the rate of expansion, template code is more and easier to forget
  3. ViewwithViewModelthroughViewModelExposed method interactions are messy and difficult to maintain

MVI can better solve the above pain points, and it has the following advantages

  1. Emphasize the one-way flow of data, making it easy to track and trace state changes
  2. useViewStaterightStateCentralized management, only need to subscribe to oneViewStateTo get all the state of the page, relativeMVVMMuch less template code
  3. ViewModelthroughViewStatewithActionCommunicate by browsingViewStateAcitonThe definition is clearViewModelCan be used directly as an interface document.

Of course, MVI also has some disadvantages, such as

  1. All operations will eventually be converted toState, so when complex pagesStateEasy to swell
  2. stateIs constant, so every timestateWhenever an update is required, a new object is created to replace the old object, which incurs some memory overhead

There is no silver bullet in software development. All architectures are not perfect and have their own scenarios that readers can choose to use according to their own needs. However, based on the above analysis and introduction, I believe that using MVI architecture instead of MVVM without DataBinding is a better choice

Samplecode

Github.com/shenzhen201…

The resources

Android BASED MVI architecture: How to choose from bidirectional binding to one-way data flow Jetpack Compose architecture? MVX Best Architecture For Android: MVI + LiveData + ViewModel = ❤️