For the traditional way of Android code writing, generally, the page UI processing, data loading, all in the Activity or Fragment, but this does not meet the “single function principle”, is not easy to maintain and expand. The traditional MVC, MVP and MVVM divide the project structure into three layers, “one for each management”. These three modes have their own characteristics and advantages and disadvantages, but they all have one thing in common, that is, they distinguish M layer from V layer, M layer is Model layer, V layer is View layer, and M layer is responsible for data processing. The View layer is responsible for displaying the UI. The difference lies in how the M layer and THE V layer are combined.

Among them, MVVM mode in addition to M layer and V layer, is VM layer, namely ViewModel.

Jetpack provides developers with the concept of a ViewModel that separates the data needed for a page from the V and M layers. The ViewModel is a bridge between the View and Model layers that allows views and data to be separated and connected.

The life cycle

When an Android application returns to the desktop or switches between vertical and horizontal screens, components such as an Activity can lose state or be destroyed, and developers often need to consider saving and restoring data. The most common methods are onSavaInstanceState() and onRestoreInstanceState(). With ViewModel, you can save data in a much simpler way. Why is that?

The ViewModel is independent of component configuration changes, that is, the ViewModel lifecycle does not change when special circumstances occur that cause the Activity to re-execute some lifecycle.

The following diagram shows the relationship between the ViewModel and the Activity lifecycle:

As you can see from the figure above, the ViewModel follows the Activity through its life cycle and does not clear until after the Activity executes onDestroy().

Method of use

The first step is to add a dependency

/ / the ViewModel implementation 'androidx. Lifecycle: lifecycle - ViewModel: 2.2.0'Copy the code

Then create MyViewModel class, which inherits from ViewModel:

class MyViewModel : ViewModel() {

    override fun onCleared() {
        super.onCleared()
        print("onCleared")
    }
}
Copy the code

As you can see, the ViewModel class has only one lifecycle method, onCleared(), and we usually need to do some resource release in this method to avoid memory leaks.

Note that the onCleared() is not executed when the Activity lifecycle changes. To prove that the ViewModel does not change as the Activity executes its life cycle, we can do a timed loop in the ViewModel using either Handler or RxJava to see if the Activity affects the ViewModel:

class MyViewModel : ViewModel() {

    var handler: Handler = object : Handler() {
        var i = 0
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            print(i++)
            sendEmptyMessageDelayed(0, 500)
        }
    }

    public fun startHandler() {
        handler.sendEmptyMessageDelayed(0, 500)
    }

    override fun onCleared() {
        super.onCleared()
        print("onCleared")
        handler.removeMessages(0)
    }
}
Copy the code

Using Handler in MyViewModel, we create a timed rotation task that prints logs every 500 milliseconds. Next, create the ViewModel instance object in the Activity and call its startHandler() method to start executing:

class ViewModelActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)

        val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
        viewModel.startHandler()
    }
}
Copy the code

Running the code will find that the log does not break when the Activity rotates and that the printed numbers are continuous, proving that the ViewModel is not affected by the Activity’s life cycle.

Create a ViewModelProvider object. The constructor of the object needs to pass in the Activity instance. FragmentActivity implements the ViewModelStoreOwner interface by default:

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {

}
Copy the code

After creating the ViewModelProvider instance, call its get() method:

public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); if (oldViewModel ! = null) { oldViewModel.onCleared(); } } final ViewModel get(String key) { return mMap.get(key); } Set<String> keys() { return new HashSet<>(mMap.keySet()); } /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { for  (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); }}Copy the code

The get() method calls the get() method of ViewModelStore. The ViewModelStore class uses HashMap to store the ViewModel and its key (class name).

other

Note that precisely because the ViewModel is outside the Activity lifecycle, it is not recommended to pass a Context reference into the ViewModel to avoid memory leaks. But what if you have to use a Context in a ViewModel, you can inherit the ViewModel class from AndroidViewModel, and AndroidViewModel inherits from the ViewModel, and take the Applcation Context.

ViewModel is not affected by the Actvity lifecycle. Can we use ViewModel instead of onSaveInstanceState()? ViewModel differs from onSaveInstanceState() in that the onSaveInstanceState() method is used to store a small amount of state data and can be persisted, but ViewModel has no limit on the size of the data. When the page is completely destroyed, The data in the ViewModel is no longer there.