Android MVVM之ViewModel的详解与使用

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、介绍

              ViewModel 类是一种业务逻辑或屏幕级状态容器。它用于将状态公开给界面以及封装相关的业务逻辑。 它的主要优点是它可以缓存状态并可在配置更改后持久保留相应状态。这意味着在 activity 之间导航时或进行配置更改后例如旋转屏幕时界面将无需重新提取数据。

ViewModel 的优势

        ViewModel 的替代方案是保存要在界面中显示的数据的普通类。在 activity 或 Navigation 目的地之间导航时这可能会造成问题。此时如果您不利用保存实例状态机制存储相应数据系统便会销毁相应数据。ViewModel 提供了一个便捷的数据持久性 API可以解决此问题。

ViewModel 类的主要优势实际上有两个方面

  • 它允许您持久保留界面状态。
  • 它可以提供对业务逻辑的访问权限。

持久性

        ViewModel 允许数据在 ViewModel 持有的状态和 ViewModel 触发的操作结束后继续存在。这种缓存意味着在常见的配置更改例如屏幕旋转完成后您无需重新提取数据。

作用域

实例化 ViewModel 时您会向其传递实现 ViewModelStoreOwner 接口的对象。它可能是 Navigation 目的地、Navigation 图表、activity、fragment 或实现接口的任何其他类型。然后ViewModel 的作用域将限定为 ViewModelStoreOwner 的 Lifecycle。它会一直保留在内存中直到其 ViewModelStoreOwner 永久消失。

有一系列类是 ViewModelStoreOwner 接口的直接或间接子类。直接子类为 ComponentActivityFragment 和 NavBackStackEntry

当 ViewModel 的作用域 fragment 或 activity 被销毁时异步工作会在作用域限定到该 fragment 或 activity 的 ViewModel 中继续进行。这是持久性的关键。

实现 ViewModel

        viewModel是一个类我们在使用的时候只要继承就可以了但是viewModel和UI数据更新是通过LiveData这就是所谓的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的创建

        使用viewModel的核心是想通过viewModel来管理生命周期所以创建的时候不是通过直接new出来而是有专门的工具ViewModelProvider

ViewModelProvider使用

两种构造如下

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

        因为fragment和Activity内部已处理了ViewModelStoreOwner所以可以借助ViewModelProvider直接使用

正常使用

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

如果需要factory创建可以使用factory from的方法

companion object {

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

小试牛刀

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()
        }
    }
}
 结果

 注意

        ViewModel通常不应引用视图、Lifecycle 或可能存储对 activity 上下文的引用的任何类。由于 ViewModel 的生命周期大于界面的生命周期因此在 ViewModel 中保留与生命周期相关的 API 可能会导致内存泄漏。

ViewModel 的生命周期       


         按照依赖项注入的最佳实践ViewModel 可以在其构造函数中将依赖项作为参数。这大多是网域层或数据层中的类型。由于框架提供 ViewModel因此需要一种特殊机制来创建 ViewModel 的实例。该机制是 ViewModelProvider.Factory 接口。只有此接口的实现才能在适当的作用域内实例化 ViewModel。

包含 CreationExtras 的 ViewModel

        如果 ViewModel 类在其构造函数中接收依赖项请提供用于实现 ViewModelProvider.Factory 接口的工厂。替换 create(Class<T>, CreationExtras) 函数以提供 ViewModel 的新实例

借助 CreationExtras您可以访问有助于实例化 ViewModel 的相关信息。下面列出了可以通过 extra 访问的键

功能
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY提供对您传递给 ViewModelProvider.get() 的自定义键的访问权限。
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY提供对 Application 类实例的访问权限。
SavedStateHandleSupport.DEFAULT_ARGS_KEY提供对您在构造 SavedStateHandle 时应使用的参数 bundle 的访问权限。
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY提供对用于构造 ViewModel 的 SavedStateRegistryOwner 的访问权限。
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY提供对用于构造 ViewModel 的 ViewModelStoreOwner 的访问权限。

如需创建 SavedStateHandle 的新实例请使用 CreationExtras.createSavedStateHandle().createSavedStateHandle()) 函数并将其传递给 ViewModel。

ViewModel 作用域 API

作用域是有效使用 ViewModel 的关键。每个 ViewModel 的作用域都限定为一个实现 ViewModelStoreOwner 接口的对象。有多个 API 可帮助您更轻松地管理 ViewModel 的作用域。本文档简要介绍了您应该了解的一些关键技术

ViewModel 的作用域限定为最近的 ViewModelStoreOwner

 可以指定哪个fragment或者Activity的主题

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

ViewModel 的作用域限定为 Navigation

关于Navigation怎么使用可以看我的一篇文章

Android 导航之Navigation 组件的介绍与使用

        Navigation 图也是 ViewModel Store Owner。如果您使用的是 Navigation Fragment 或 Navigation Compose可以使用 navGraphViewModels(graphId)

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

源码分析

 在绑定完Navigation其内部已支持存储ViewModelStore

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

以及状态保存saveStateHandle

 

Navigation的生命周期同样可以同步宿主的生命周期。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android