Managing and interface related data in a life-cycle oriented manner, Jetpack brings us the ViewModel, and in this article you can learn the proper posture to use the ViewModel.

A, the ViewModel

Viewmodels are used to hold data and manage data related to the interface in a life-cycle oriented manner. You can also save data when the relevant configuration changes, such as screen rotation.

Since the Android Framework manages UI controllers with life cycles such as activities and fragments, certain user actions or events distributed by the system can cause the framework to destroy and rebuild UI controllers without our control. For example, when the screen rotates, FramWork calls the Activity’s onSaveIntanceState() method to save data, which is then recovered in the new Activity’s onCreate() method, but only for small pieces of data that can be serialized or deserialized. Big data like lists or Bitmaps are not appropriate. In addition, a large amount of existing data will be destroyed and then regenerated, resulting in a waste of resources.

LiveData also said that do not do a lot of logical operations in activities and fragments, which will lead to bloated code and difficult to maintain. It is recommended to separate the related logic to a separate class for maintenance, and let the UI controller take care of their essential work.

To do this, the ViewModel can easily solve the above problems.

1. Realize the ViewModel

Architecture Components provides ViewModel utility classes that provide data to the UI. The ViewModel object automatically saves data when configuration changes and is used in new Activity or Fragment instances. For example, if you need to display a list that holds multiple Users objects in your APP, you should implement the action of requesting and saving users data in a ViewModel object instead of an Activity or Fragment.

class MyViewModel : ViewModel() {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData().also {
            loadUsers()
        }
    }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}
Copy the code

Accessing data in an Activity:

class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {// Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. val model = ViewModelProviders.of(this).get(MyViewModel::class.java) model.getUsers().observe(this, Observer<List<User>>{ users -> // update UI }) } }Copy the code

When the Activity is rebuilt, it receives the ViewModel object from the first Activity, so the data does not change. When an Activity is destroyed, the Framework automatically calls the onCleared() method on the ViewModel object to release the resource. Remember that the ViewModel must not hold references to a View, Lifecycle, or any Activity context object. Because viewModels have a longer lifetime than they do, they can cause memory leaks. If the ViewModel needs to hold the application’s ConetXT, for example to get system services, it can inherit the AndroidViewModel class and use its constructor to receive the application’s context.

If Android Studio can’t find ViewModelProviders, add the following dependencies.

 api "Android. Arch. Lifecycle: extensions: 1.1.1"
Copy the code

2. ViewModel lifecycle

ViewModelProvider get ViewModel object, scope will be passed to the ViewModel life cycle ViewModelProvider, ViewModel will remain in memory, scope failure until its life cycle. The ViewModel object lifecycle is broken in two cases: finished as an Activity object and detached as a Fragment object.

The following figure shows the Activity lifecycle and the scope of the ViewModel. The first column shows the state of the Activity object, the second column shows the Activity lifecycle methods, and the third column shows the scope of the ViewModel.

onCreate()
onCreate()

3. Share data between fragments

In real development, it is common to share data between two or more Fragement objects, usually by actually defining the interface and binding the Activity to the Fragment. In addition, the Activity also handles the creation and visibility of fragments.

Fragments can share the same ViewModel object within the scope of an Activity to handle this cumbersome data interaction. Such as:

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() { private lateinit var itemSelector: Selector private lateinit var model: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) model = activity? .run { ViewModelProviders.of(this).get(SharedViewModel::class.java) } ?: throw Exception("Invalid Activity")
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() { private lateinit var model: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) model = activity? .run { ViewModelProviders.of(this).get(SharedViewModel::class.java) } ?: throw Exception("Invalid Activity")
        model.selected.observe(this, Observer<Item> { item ->
            // Update the UI
        })
    }
}
Copy the code

Since both Fragment objects are bound to the same Activity object, they get the same ViewModel object in the scope of the Activity through the ViewModelProvider.

The benefits of this are:

  • The Activity object doesn’t sense the interaction between two Fragment objects, so it doesn’t have to do anything.
  • The Fragment object does not need to know about another Fragment object, except by convention with the ViewModel. When one object disappears, the other object can still be used.
  • The life cycle of the Fragment object is not affected by other objects, one Fragment object replaces another, and the UI still works.

4. ViewModel replaces Loader

Loader classes like CursorLoader are often used to keep data in the UI in sync with the database. You can replace the Loader class with ViewModel and other classes. Using the ViewModel to separate the data loading actions of the UI controller means that more class strong references can be reduced. A common way to use Loader is to use a CursorLoader in the application to observe the contents of the database. When the value of the database changes,Loader automatically reloads the data and updates the user interface:

More information can be found on our website

conclusion

At this point, Jetpack basically understands what Jetpack is all about. Jetpack summarizes the various efficiencies and architectures we normally develop to provide us with more standard components. Room operates on the database, LiveData notifies data changes, DataBinding updates the View,Lifecycle management cycles, and more. Are defining a set of uniform development architecture standards for our application layer development so that we can develop better apps. With such a set of architecture components, it is possible to develop an APP with high maintainability and good scalability quickly.

Welcom to visit my github

This is the fourth article in the Jetpack series

Jetpack: Are you still in findViewById

Jetpack: How do you manage the Activity and Fragment lifecycle

Jetpack: How to gracefully update Views as data Changes