Introduce the ViewModel

The ViewModel strips the data needed for the page from the page, which only needs to handle user interaction and present the data. Is a bridge between View (UI) and Model (data), allowing views and data to remain separate and communicate. The ViewModel is independent of configuration changes, such as when the activity rotates the page is rebuilt, and the lifecycle is restarted after the activity completes, but this does not affect the ViewModel lifecycle, the same ViewModel. The instantiation process of a ViewModel is completed by the ViewModelProvider. The ViewModelProvider will determine whether the ViewModel exists. If the ViewModel exists weakly, it will return directly.

The ViewModel instantiation

val timerViewModel = ViewModelProvider(this).get(TimerViewModel::class.java)
Copy the code

The ViewModelProvider method requires a ViewModelStoreOwner parameter,

   public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
Copy the code

We use the current page context this as the ViewModelProvider argument, because under the AndroidX package our activity eventually inherits ComponentActivity, This activity already implements the ViewModelStoreOwner interface by default, which defines the getViewModelStore method and returns the ViewModelStore object

@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}
Copy the code

ViewModel is maintained in the ViewModelStore object. You can see that the ViewModel is actually cached in memory as HashMap<String, ViewModel>, so it’s independent of the activity lifecycle. Do not pass in any Context or reference to an object with a Context when using a viewModel. This will cause the page to be unable to be destroyed, causing a memory leak. But it is not released in time, and the memory will remain occupied and cannot be allocated.)

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

If you must use context in a ViewModel, you can use the AndroidViewModel class, which is a subclass of ViewModel and receives Application as context, which means that its life cycle is the same as that of Application.

ViewModel source code creation

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { if (mFactory instanceof OnRequeryFactory) { ((OnRequeryFactory) mFactory).onRequery(viewModel); } return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel ! = null) { // TODO: log a warning. } } if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) mFactory).create(key, modelClass); } else { viewModel = mFactory.create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; }Copy the code

use

Rely on

Implementation 'androidx. Lifecycle: lifecycle - viewmodel - KTX: 2.3.0'Copy the code

Viewmodel is an abstract class that maintains an onCleared() method, which is called when the ViewModel is no longer needed and the associated activity is destroyed. Some resource frees can be performed, but the re-creation of the activity due to screen rotation does not trigger this method and does not affect the viewModel life cycle

TimerViewModel, which implements viewModel, maintains a timer in it, and instantiates it in the activity to see if the timer resets when the application switches between foreground and background and between vertical and horizontal screens.

class TimerViewModel:ViewModel() { private var timer:Timer? =null private var currentSecond:Int = 0 fun startTiming(){ if (timer==null){ currentSecond = 0 timer = Timer() val timerTask:TimerTask = MyTask() timer? .schedule(timerTask,1000,1000)}} private inner class MyTask: timerTask (){override fun run() {currentSecond ++ if (onTimeChangeListener! =null){ onTimeChangeListener.onTimeChange(currentSecond) } } } interface OnTimeChangeListener{ fun onTimeChange(second:Int) } private lateinit var onTimeChangeListener:OnTimeChangeListener fun setOnTimeChangeListener(onTimeChangeListener:OnTimeChangeListener){ this.onTimeChangeListener = onTimeChangeListener } /** * This method is called by the system when the viewModel is no longer needed, that is, when the associated activity is destroyed. * It can perform some resource release operations, but rebuilding the activity due to screen rotation does not trigger this method. */ Override fun onCleared() {super.oncleared ()}}Copy the code

The activity is associated with the ViewModel

 private fun init() {
        val timerViewModel = ViewModelProvider(this).get(TimerViewModel::class.java)
        timerViewModel.setOnTimeChangeListener(object : TimerViewModel.OnTimeChangeListener {
            override fun onTimeChange(second: Int) {
                Log.e(TAG, "onTimeChange:$second ")
            }
        })
        timerViewModel.startTiming()
    }
Copy the code

The results show that the viewModel life cycle does not change when the activity is in the background and switches between vertical and horizontal screens

ViewModel vs. onSaveInstanceState()

The onSaveInstanceState() method can also solve the problem of losing data due to screen rotation, but onSaveInstanceState() can only hold a small amount of serializable data, whereas ViewModel does not have this limitation. The onSaveInstanceState() method does not have this limitation. The onSaveInstanceState() method does not have this limitation.

LiveData introduction

LiveData is a data class container that can be observed. It can wrap the data and become an observer. When the data changes, it can be captured by the observer. LiveData is an abstract class, we usually use its subclass MutableLiveData LiveData can sense the life cycle of a page, detect whether the current page is active state, only the active state can receive notification, otherwise it will be destroyed, binding activity process is as follows

MutableLiveData usage

private var currentSecond:MutableLiveData<Int> = MutableLiveData()
Copy the code
private fun initComponent() {
        val timerViewModel = ViewModelProvider(this).get(TimerViewModel::class.java)
        var liveData = timerViewModel.getCurrentSecond()
        liveData.observe(this, Observer {
            Log.e(TAG, "initComponent::$it ")
        })
    }
Copy the code

Example code -view_model branch