Best practices and patterns in ViewModel + Data Bi

2019-04-11 23:52发布

问题:

Looking through the samples I seen 2 approaches to MVVM using Android Architecture Components.

First approach:

  1. ViewModel provides LiveData
  2. Activity subscribes to LiveData
  3. When observer called Activity is setting data to ViewModel ObservableField.
  4. Whole ViewModel is passed to binding.
  5. In xml you just set ObservableField as value

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        app:visibleGone="@{viewmodel.listLoading}"/>
    
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:refreshing="@{viewmodel.listRefreshing}"
        app:onRefreshListener="@{() -> viewmodel.refreshList()}"
        app:visibleGone="@{!viewmodel.listLoading}">
    

Pros: I don't need to pass state (for example "loading"), as I update listLoading ObservableField in ViewModel as this:

val listLoading = ObservableBoolean(false)
/** other observable fields go here **/

val list: MutableLiveData<List<Item>> = MutableLiveData()

  fun loadList() {
        listLoading.set(true)
        repo.getList { items ->
            list.value = items
            listLoading.set(false)
        }
    }

Cons: Are there any cons in this approach?

Second approach:

  1. ViewModel provides LiveData
  2. Activity subscribes to LiveData
  3. When observer called Activity is passed to binding
  4. Only needed object (pojo) is passed to binding

Pros: Any pros of this approach?

Cons: State should be returned from ViewModel. In this sample from Google data is wrapped in Resource object.

First approach is used in another sample app from Google

I would like to know what are pros and cons of both patterns from developers with more experience working with Android Data Binding and Android Arch Components.

回答1:

You should consider splitting view-logic with business-logic.

Since you have a ViewModel using databinding and AAC to handle that you should also seperate the logic inside your view (layout).

Just pass two variables to your layout. One is the VievModel which handle business-logic like pressing a button and processing the logic, the second one is the View (fragment).

After that you can use

app:onRefreshListener="@{() -> yourViewFragment.refreshList()}"

and avoid having "context leaks" or a not working solution if there's currently no view subscribed.

Since the onRefreshListener is bound to a fragment its OK to pass that inside your fragment.

You shoudnt create a LiveData or ObservableField inside your ViewModel to handle that kind of operations because if you pause and resume the fragment you'r going to observe the LiveData again. That also means that you will get the last data delivered again.

Example which can be used in the ViewModel:

<Textview ... name="@{viewModel.dataOfYourModel}" onClick="@{viewModel.doNetworkCall}" />

Golden rule: Every package/import beginning with android.* should be NOT inside the viewmodel except the android.arch.* components.