The purpose of this article

Lifecycle ViewModel LiveData based MVVM framework has been available from Google for some time and I have recently tried to use it in my team. There have been some detdetments along the way.

The key content

The official documents of these three things are not satisfactory, leading to some difficulty in getting started. More people are afraid to try them. The main reason is that they do not know how much benefit these things can bring to actual developers, such as whether they can save workload, improve code quality, and solve the drawbacks of MVC and MVP. This article will focus on these aspects.

The preparatory work

This code is all implemented in Kotlin language. The build file needs to be updated

Dependencies {def lifecycle_version = "2.3.0" def arch_version = "2.1.0 "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" // LiveData implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" // Lifecycles only (without ViewModel or LiveData) implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" // Saved state module for ViewModel implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" // Annotation processor annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" // alternately - if using Java8, use the following instead of lifecycle-compiler implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" // optional - helpers for implementing LifecycleOwner in a Service implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version" // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version" // optional - ReactiveStreams support for LiveData implementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version" // optional - Test helpers for LiveData TestImplementation "androidx.arch.core:core-testing:$arch_version "Androidx. Activity: activity - KTX: 1.2.2," implementation "androidx. Fragments: fragments - KTX: 1.3.2"}Copy the code

LifeCycle

LifeCycle will not, strictly speaking, be used directly within the Android MVVM framework, most of the time working with ViewModels and LiveData. But LifeCycle is fundamental to the implementation of the first two. And in many cases LifeCycle will make our code simpler without using the Android MVVM

For example, we sometimes write a class that constantly gets location information as a basic API for everyone to call. The pseudo-code is as follows:

class MyLocationListener(val context: Context, val callback: (Int) -> Unit) {fun start() {fun stop() {fun stop() {Copy the code

For the caller, we definitely need to call the listener’s start method in the activity’s onStart method to start the lookup and manually call the stop method in the onStop method to stop the lookup otherwise the lookup will consume power and your boss will disallow you.

But that brings up a problem. As a base class, isn’t it too cumbersome to ask your caller to manually add some code to onStart onStop? And what if someone forgets to call the stop method?

Or consider a slightly more complex scenario where we don’t look up information for users who are not logged in, and the code looks like this:

Override fun onStart() {super.onstart () //checkLoginStatus is an IO query that takes some time (result) { listener? .start() } } }Copy the code

What’s wrong with this code? Because the query operation is a time consuming operation, when the query results after the callback, ** how do you ensure that the page is still there when the callback, rather than the user click in and immediately quit, when you call back the interface is over? 六四屠杀

Of course you can solve the above problem by adding some code to determine the state of your activity, but this will make it more expensive for others to access your component

So how do you solve these problems?

Why not make my component aware of the life cycle of the activity Fragment Service?

This is the primary role that Lifecycle will play

Let’s look at the specific use of Lifecycle for this scenario

The main thing is to implement the LifecycleObserver interface. The second thing is to use the OnLifecycleEvent annotation to indicate which lifecycle you want your method to be called in

class MyLocationListener(val context: Context, val callback: (Int) -> Unit) : LifecycleObserver {@onlifecycleEvent (Lifecycle.event.on_start) fun start() {// Start to find the location information and call the callBack The activity Log interface. V (" wuyue ", "Listerner start")} @onlifecycleEvent (Lifecycle.event.on_stop) fun stop() {log.v ("wuyue", "listerner stop") } }Copy the code

Then look at the callers:

class MainActivity : AppCompatActivity() { private var listener: MyLocationListener? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) listener = MyLocationListener(this) { Lifecycle. AddObserver (listener!! }}Copy the code

This way the cost to the access side is just one sentence lifecycle.addobServer () and all the rest of the lifecycle related operations can be written in your own component without error.

Lifecycle ensures that a function that you specify will only be executed during its lifecycle, otherwise it will not.

Data-driven UI

In general, the process for writing Android code is as follows:

  1. Query network I/O or local I/O to obtain data
  2. Find UI components that manually update the UI in a manner similar to setText(data)

In addition, as long as the data changes, no matter what the reason is, step 2 should be repeated. Once the omission will cause a bug. In fact, for such UI-oriented system, the current mainstream writing method is data-driven UI changes, that is to say:

As long as the data and UI component are bound properly, the UI component can automatically update the UI state according to the latest data value when the subsequent data changes

This approach to writing data-driven UI changes is at the heart of the MVVM framework

Let’s write an example to get a feel for it

Let’s say we have a field name with an initial value of vivo, and we have a button that every time we click it adds test to the end of that value and updates it to some textView to display

You can write it normally, but let’s see how we can write it MVVM style.

We first define a ViewModel and specify the name field we care about as LiveData.

In most scenarios, LiveData is defined in the ViewModel

class NameViewModel : ViewModel() {
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>("vivo")
    }
}

Copy the code

Then look at the code for the activity:

Class MainActivity: AppCompatActivity() {//viewModels activity- an extension to KTX that doesn't have to worry about anything private val model: NameViewModel by viewModels() private val nameTv: TextView by lazy { findViewById(R.id.tv1) } private val nameChangeTv: TextView by lazy { findViewById(R.id.tv2) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) nameChangeTv.setOnClickListener { Model.currentname. value = model.currentName.value + "test"} Lifecycle package val nameObserver = Observer<String> {newName -> nametv. text = newName } / / manual to bind our observer to model the viewmodel currentName this liveData. CurrentName. Observe (this, nameObserver)}}Copy the code

These are all written in stone, so you don’t need to know the implementation details right now. So what good is this?

Let’s assume that the scenario is a little bit more complicated, and this currentName can be changed in multiple places, so if you don’t write it like MVVM,

Instead of data-driven UI changes, we’ll manually add nametV.text = newName whenever we modify curentName

It’s easy to miss, I don’t want to mention it, and in case you have to change something later like you have to change the color of the font or something like that there’s too much to change,

With mVVM-like writing, you just bind the relationship between the UI observer and the data source liveData and define the internal observer

Behavior, which is the rule that the UI changes when you receive data changes.

One place to write, effective everywhere, is really cool

Also, it was mentioned earlier that our Observer is an Observer under the Androix Lifecycle package, so a smarter person can definitely figure it out

Another benefit of this is that it ensures that you are performing the UI correctly, and that you will never have to update the UI while the activity is being destroyed

Fragment and activity share data

It’s common to see a scenario where an activity contains multiple fragments that share a piece of data with the activity, and when that data changes, the UI corresponding to all the fragments changes.

Previously, we handled the corresponding scenarios through various coupled interfaces or through EventBus. It’s cumbersome, it’s very coupled, and you have to consider whether the fragment’s life cycle is correct and whether you can refresh the UI.

But the ViewModel makes handling this situation much easier.

Class MasterFragment: Fragment() {// Note that activityViewModels is a component of the activity. NameViewModel by activityViewModels() private var nameTv: TextView? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super. OnViewCreated (view, savedInstanceState) / / written note here the model currentName. Observe (viewLifecycleOwner, Observer<String> { nameTv? .text = it }) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup? , savedInstanceState: Bundle?) : View? { return inflater.inflate(R.layout.master_layout, null).apply { nameTv = findViewById(R.id.tv1) } } }Copy the code

It’s really easy to change a few parameters and share data globally within the activity’s scope. And no matter where the value of data is changed, the UI is refreshed globally.

conclusion

Lifecycle ViewModel and LiveData comprise the Android MVVM framework.

  1. Data-driven UI is easy
  2. You can safely refresh the UI without having to worry about any activity lifecycle issues after a callback