I should have written this article a long time ago, but I decided to finish it today

First of all, I think the MVVM code design in ViewModel + LiveData + XXX is basically fake (FAKE news :P).

MVVM

First, MVVM is an architectural pattern (or design pattern) that I learned about from Wikipedia

Let’s look at the standard definitions for MVVM components

  • model: Nothing to say, withMVP MVCThe flow defines the business (data) logic and the model of the data
  • View: Still nothing to say, just the view layer, the user interface
  • view-model: The key is here, what we callview-modelIn fact, from thepresenterThe glue evolved to expose the view’s public properties and directives, essentiallyviewOne corresponding to the viewmodel
    • First of all, it’s a View Model, rightmodelLayer exposure comes fromviewSome of the layer’s public properties, as well as fromviewLayer directives (Presenters?)
    • And,binder: view-modelThe difference inpresentercontrollerIt has a namebinderHandles the automatic synchronization of view attributes (states) exposed in the View-Model with the view UI

So the flow path in MVVM mode should look like this:

  • view: user input event -> view-model: view property or command
  • view-model: handle input, biz model -> model: biz data/logic processing
  • model: state chagne events(data) -> view-model: handle biz state
  • view-model: ui state update -> binder: handle ui state synching

MVP

Ok, let’s take a look at how MVP flows again

  • view: User input event -> presenter: function (command)
  • presenter: biz model -> model: biz data/logic processing
  • model: state change events(data) -> presenter: convert biz state
  • presenter: biz state/data -> view: refresh ui

Take a closer look at the flow of the pseudo-MVVM (MVP) based on the Jetpack ViewModel + LiveData stream:

  1. Activity/Fragemnt -> ViewModel, this isview -> presenter
  2. ViewModelcallmodelHandle network requests, data logic, file IO and other business logic, which ispresenter -> model
  3. So let’s take a look at theViewModelThe business logic notification is completed inUIRefresh, throughLiveDatasetValue/postValueUpdate status,

The view layer by the viewModel. XxxLiveData. Observe (lifecycleOwner) {data – >… }

In the last section, we compare the classic MVP mode of writing: Presenter notifies a view of a change through its holding view interface, and the View layer updates the UI components in the corresponding interface implementation

Even though the LiveData observer mode is used to observe data in the view layer, eliminating the definition and coupling of the view interface in the classic MVP writing method, the event (data) flow path is still going in MVP mode

Comparing the workflow defined by MVVM, one of the biggest differences is the role of the Binder as an important component for data and UI synchronization and as an internal member of the View-Model, as defined by the MVVM pattern

Therefore, it can be concluded that the key to MVVM is that the flow of user events is one-way, starting at the View layer and ending at the View-Model; And the key is binder

Jetpack data-binding

Databinding is an official Binder implementation provided by Google Dad

That is:

  • MVVMIn thebinderYou can directly use the officialdata-bindingComponent to implement
  • Do not usedata-bindingComponent, custom processing data withUIThe synchronousbinderAnd in theview-modelMaintenance, is also the standard MVVM writing method

DataBinding is divided into two parts

ViewDataBinding: each layout > tags package layout will generate a ViewDataBinding subclass as caretakers of the view and the data binding observables/BaseObservable: The actual binder development interface, the data that needs to be bound to the view layer can be automatically monitored and synchronized by implementing this interface and registering members (the actual code is in the generated XxxLayoutBindingImpl class)

The use and principles of DataBinding are not described here

The landing of MVVM in the actual project

sample Activity

class SampleBindingActivity : AppCompatActivity(), ActivityBindingHolder<SampleActivityBinding> by ActivityBinding(R.layout.sample_activity) {

    private val viewModel: SampleViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?). {
        // replace setContentView(), and hold binding instance
        inflateBinding { binding ->
            // init with bindingBinding. InitView ()... The viewModel. Bind (binding)}... }private fun SampleActivityBinding.initView(a) {
        val random = Random()
        btnTest.onClick {
            viewModel.random = random.nextInt(100)}}}Copy the code

sample layout

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="binder"
            type="package.SampleBinder" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/tv_nickname"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:text="@{binder.nickname}"
            android:textSize="12sp"
            app:layout_constraintBottom_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="@id/channel_name"
            app:layout_constraintStart_toStartOf="@id/program_vip" />

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btn_test"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_nickname" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Copy the code

sample view-model

class SampleViewModel : ViewModel() {

    var random: Int by ObservableProperty(0) { binder.nickname = "Nickname_$it" }

    private val model = SampleModel() // biz handler model, network/data/io etc.
    private val binder = SampleBinder() // binder for sync data and view state

    fun bind(binding: ViewDataBinding){binding. SetVariable (br. binder, binder)}... }Copy the code

sample binder

class SampleBinder : BaseObservable() {

    @get:Bindable
    var nickname: String by observableField(BR.nickname, "Nickname")... }Copy the code

Note:

ActivityBindingHolder 👉🏻 Android-Zanpakuto: ActivityBinding. Kt func: viewModel 👉🏻 Android-Zanpakuto: Viewmodel.kt func: ObservableProperty 👉🏻 Android-zanpakuto: ObservableProperty. Kt func: ObservableProperty. ObservableField 👉 🏻 android – zanpakuto: ObservableAttribute kt