Detailed explanation and use of ViewModel of Android MVVM

1. Introduction

              The ViewModel class is a kind of business logic or screen-level state container. It is used to expose state to interfaces, and to encapsulate related business logic. Its main advantage is that it caches state and persists it across configuration changes. This means that the interface will not need to refetch data when navigating between activities or after making configuration changes, such as when the screen is rotated.

Advantages of ViewModels

        An alternative to ViewModel is a normal class that holds the data to be displayed in the interface. This can cause problems when navigating between activities or Navigation destinations. At this point, if you do not store the corresponding data using the save instance state mechanism, the system will destroy the corresponding data. ViewModel provides a convenient data persistence API that can solve this problem.

The main advantage of the ViewModel class is actually twofold:

  • It allows you to persist UI state.
  • It can provide access to business logic.

Persistence

        The ViewModel allows data to survive the state held by the ViewModel and the actions triggered by the ViewModel. This caching means that you don't need to re-fetch data after common configuration changes, such as screen rotations, are made.

scope

When you instantiate a ViewModel, you pass it an object that implements  the ViewModelStoreOwner  interface. It could be a Navigation Destination, Navigation Chart, Activity, Fragment, or any other type that implements the interface. The ViewModel will then be scoped to  ViewModelStoreOwner the  Lifecycle . It remains in memory until it is  ViewModelStoreOwner permanently gone.

There are a series of classes that are  ViewModelStoreOwner direct or indirect subclasses of an interface. Direct subclasses are  ComponentActivity , Fragment  , and  NavBackStackEntry .

When the ViewModel's scoped fragment or activity is destroyed, asynchronous work continues in the ViewModel scoped to that fragment or activity. This is the key to persistence.

Implement ViewModel

        ViewModel is a class. When we use it, we only need to inherit it, but viewModel and UI data are updated through LiveData, which is the core of the so-called MVVM.

class DiceRollViewModel : ViewModel() {


    private  var data: MutableLiveData<String>?=null
    fun getData(): MutableLiveData<String> {
        if (data == null) {
            data = MutableLiveData()
        }
        return data!!
    }


    fun  updateValue(){
        val value=kotlin.random.Random.nextInt(1,100);
        data?.value="随机生成数为${value}"
    }
}

ViewModel creation

        The core of using viewModel is to manage the life cycle through viewModel, so when creating it, it is not through direct new, but there is a special tool ViewModelProvider

ViewModelProvider: use

The two constructs are as follows:

  1. public constructor(owner: ViewModelStoreOwner)
  2. public constructor(owner: ViewModelStoreOwner, factory: Factory) 

        Because ViewModelStoreOwner has been processed inside fragment and Activity, it can be used directly with the help of ViewModelProvider

Normal use:

viewModel = ViewModelProvider(this).get(DiceRollViewModel::class.java)

If you need factory creation, you can use the factory from method

companion object {

    @JvmStatic
    fun from(vararg initializers: ViewModelInitializer<*>): Factory =
        InitializerViewModelFactory(*initializers)
}

Small scale chopper:

class TestViewModelActivity : BaseActivity() {

    private lateinit var bind: MyVideModelBind
    private lateinit var viewModel: DiceRollViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        bind = DataBindingUtil.setContentView(this, R.layout.layout_viewmodel_demo)
        viewModel = ViewModelProvider(this).get(DiceRollViewModel::class.java)
        viewModel.getData().observe(this, Observer {
            bind.value = it
        })

        bind.btnUpdate.setOnClickListener {
            viewModel.updateValue()
        }
    }
}
 result

 Note :

        ViewModels should generally not have references to views, lifecycles  , or any classes that might store references to the activity context. Since the ViewModel's lifecycle is greater than that of the interface, keeping lifecycle-related APIs in the ViewModel can lead to memory leaks.

ViewModel life cycle       


         Following dependency injection best practices, ViewModels can take dependencies as parameters in their constructors. This is mostly the type in the domain layer or data layer. Since the framework provides the ViewModel, a special mechanism is required to create an instance of the ViewModel. The mechanism is the ViewModelProvider.Factory interface. Only implementations of this interface can instantiate ViewModels within the appropriate scope.

ViewModel containing CreationExtras

        If the ViewModel class receives dependencies in its constructor, provide a factory that implements the ViewModelProvider.Factory interface. Replace create(Class<T>, CreationExtras) function to provide new instance of ViewModel

With CreationExtras, you can access relevant information that helps instantiate the ViewModel. The keys that can be accessed via extra are listed below:

key Function
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY Provides  ViewModelProvider.get() access to the custom key you pass to .
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY Provides  Application access to class instances.
SavedStateHandleSupport.DEFAULT_ARGS_KEY Provides  SavedStateHandle access to the parameter bundle that you should use when constructing.
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY Provides   access ViewModel to  the .SavedStateRegistryOwner
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY Provides   access ViewModel to  the .ViewModelStoreOwner

To create a new instance of SavedStateHandle, use the CreationExtras.createSavedStateHandle().createSavedStateHandle()) function and pass it to the ViewModel.

ViewModel Scope API

Scope is the key to using ViewModel effectively. Each ViewModel is scoped to an object that implements  the ViewModelStoreOwner  interface. There are several APIs that help you manage the scope of ViewModels more easily. This document briefly introduces some key technologies that you should know about

The ViewModel is scoped to the nearest ViewModelStoreOwner

 Which fragment or activity theme can be specified

 val viewModel: MyiewModel by viewModels(
        ownerProducer = { requireParentFragment() })

ViewModel is scoped to Navigation

About how to use Navigation, you can read one of my articles:

Introduction and use of the Navigation component of Android navigation

        The Navigation graph is also the ViewModel Store Owner. If you are using Navigation Fragment or Navigation Compose, you can use navGraphViewModels(graphId)

val viewModel: MyViewModel by viewModels(
        { findNavController().getBackStackEntry(R.id.nav_graph) }
    )

Source code analysis:

 After binding Navigation, it already supports storage, ViewModelStore

private final HashMap<UUID, ViewModelStore> mViewModelStores = new HashMap<>();

and state save saveStateHandle

 

The life cycle of Navigation can also synchronize the life cycle of the host.

Guess you like

Origin blog.csdn.net/qq36246172/article/details/128549777