Android MVVM之ViewModel的详解与使用

news2025/1/20 18:35:15

一、介绍

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

ViewModel 的优势

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

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

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

持久性

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

作用域

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

有一系列类是 ViewModelStoreOwner 接口的直接或间接子类。直接子类为 ComponentActivity、Fragment 和 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的生命周期,同样可以同步宿主的生命周期。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/161332.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【UE4 第一人称射击游戏】45-使用线追踪进行破坏

上一篇&#xff1a;【UE4 第一人称射击游戏】44-瞄准时的武器线追踪步骤&#xff1a;打开“Weapon_Base”删除打印节点添加如下节点&#xff0c;表示追踪线命中目标时执行的逻辑对上面逻辑的解释&#xff1a;首先追踪线命中目标后&#xff0c;显示红色的那个准心然后让目标的健…

阿里云 - MaxCompute研究

一、官方介绍MaxCompute是适用于数据分析场景的企业级SaaS&#xff08;Software as a Service&#xff09;模式云数据仓库&#xff0c;提供离线和流式数据的接入&#xff0c;支持大规模数据计算及查询加速能力。MaxCompute适用于100 GB以上规模的存储及计算需求&#xff0c;最大…

全国青少年软件编程(Scratch)等级考试一级考试真题2022年12月——持续更新.....

1.小明想在开始表演之前向大家问好并做自我介绍,应运行下列哪个程序?( ) A. B. C. D. 正确答案:D 答案解析: 外观积木配合显示时间,才能看清楚内容。 2.舞台有两个不同的背景,小猫角色的哪个积木能够切换舞台背景?( ) A.<

UVC静态杀菌模组的工作原理及应用

现代紫外线消毒技术是基于现代防疫学、光学、生物学和物理化学的基础上&#xff0c;利用特殊设计的高效率&#xff0c;高强度和长寿命的C波段紫外光发生装置&#xff0c;产生的强紫外C光照射空气或物体表面&#xff0c;当空气或固体表面中的各种细菌、病毒、寄生虫、水藻以及其…

C/C++ 三维数组和二维数组指针的结合

示例程序&#xff1a;#include <iostream> #include <stdio.h> int main() {int a[3][4] {{1,2,3,4},{2,3,4,5},{3,4,5,6}};int b[3][4] {{10,11,12,13},{11,12,13,14},{12,13,14,15}};int(*aa[2])[4] { a,b };int* p1[3] {a[0],a[1],a[2]};int* p2[3] {b[0],…

小学三年级奥数(和差倍问题)

例题5&#xff1a;学校合唱团成员中,女生人数是男生的3倍,而且女生比男生多80人&#xff0c;合唱团里男生有多少人&#xff1f;女生有多少人&#xff1f;思路分析&#x1f604;&#xff1a;抓住关键语句&#xff0c;女生人数是男生的3倍&#xff0c;那么把男生看成1份&#xff…

《图机器学习》-Graph as Matrix:Page Rnak,

Graph as Matrix一、Graph as Matrix二、PageRank三、PageRank&#xff1a;How to solve&#xff1f;四、Random Walk with Restarts and Personalized PageRank五、Matrix Factorization and Node Embedding一、Graph as Matrix 本小节将从矩阵的角度研究图形分析和学习。 把…

centos 一个ip绑定双网卡

nmcli con show (绿正常&#xff0c;黄白不正常) nmcli con del uuid &#xff08;eg&#xff1a;nmcli con del 585bdacc-314f-423e-a935-18295d0fb48b&#xff09; nmcli con add type bond ifname bond0 mode active-backup &#xff08;bond0只是一个名称&#xff0c;可以…

操作系统导论-并发

操作系统导论-并发 一.并发 操作系统为了进程能有多个执行点&#xff0c;为进程提供了一种抽象&#xff1a;线程。线程与进程类似&#xff0c;一个进程中的所有线程共享地址空间&#xff0c;但有自己独立的栈。 1.并发问题 线程的执行顺序也需要操作系统来进行调度。由于线程…

DeViSE:A Deep Visual-Semantic Embedding Model

这篇是2013年文章提出的DeViSEDeViSEDeViSE,主要是综合了传统视觉识别的神经网络和词向量处理word2vecword2vecword2vec中的Skip−gramSkip-gramSkip−gram模型&#xff0c;实现了一个视觉和语义兼顾的ZSLZSLZSL模型&#xff0c;取得了 较好的效果&#xff0c;时至今日&#xf…

【阶段三】Python机器学习21篇:机器学习项目实战:GBDT算法的核心思想、原理概述、原理举例与GBDT回归模型

本篇的思维导图: GBDT算法的核心思想 GBDT是Gradient Boosting Decision Tree(梯度提升树)的缩写。GBDT算法也是一种非常实用的Boosting算法,它与AdaBoost算法的区别在于:AdaBoost算法根据分类效果调整权重并不断迭代,最终生成强学习器;GBDT算法则将损失函数的…

浅聊版本发布

在一般情况下&#xff0c;升级服务器端应用&#xff0c;需要将应用源码或程序包上传到服务器&#xff0c;然后停止掉老版本服务&#xff0c;再启动新版本。但是这种简单的发布方式存在两个问题&#xff0c; &#xff08;1&#xff09;在新版本升级过程中&#xff0c;服务是会暂…

Qt 之 findChild

文章目录一、简述二、原型三、使用示例注意&#xff1a;Widget根据容器的不同寻找的方式不一样比如QWidget_3里面有LineEdit只需要直接找ui.QWidget_3但是如果是QStackWidget里面的化就是直接放ui->stackedDecive2->widget(0)里面就可以了一、简述 在Qt编程过程中&#…

Arm Linux Can

Arm Linux Can一:can-utils 安装二:can-utils 使用can网络关闭can波特率设置查询can0设备的参数设置can网络开启查询工作状态can发送数据can接受数据三:can回环测试四:C语言CAN编程初始化数据结构数据发送错误处理过滤规则回环设置五:Linux 系统中CAN 接口应用程序示例报文发送…

Spring 整合 Redis 的三个简单步骤

一、导入 Redis 依赖 1、导入 Redis 客户端 jar 包依赖 导入 jar 包是需要注意&#xff0c;Redis 是属于 CS 架构模式&#xff0c;服务端需先启动&#xff0c;然后客户端主动去连它。但是客户端怎么去连接服务端呢&#xff1f;这里有两种方式&#xff1a;一种是 Jedis 客户端…

糖果(差分约束+找最小值)

糖果 题目描述 幼儿园里有 n 个小朋友&#xff0c; lxhgww 老师现在想要给这些小朋友们分配糖果&#xff0c;要求每个小朋友都要分到糖果。 但是小朋友们也有嫉妒心&#xff0c;总是会提出一些要求&#xff0c;比如小明不希望小红分到的糖果比他的多&#xff0c;于是在分配糖果…

区块链技术相关概述

第一节区块链技术相关概述一、区块链定义区块链其实就相当于一个去中介化的数据库&#xff0c;是由一串数据块组成的。狭义&#xff1a;区块链是就是一种按照时间顺序来将数据区块以顺序相连的方式组合成的一种链式数据结构&#xff0c;并以密码学方式保证的不可篡改和不可伪造…

WebDAV之葫芦儿·派盘+MiXplorer

MiXplorer 支持WebDAV方式连接葫芦儿派盘。 手机内存不够用了?东西太多清理不过来?快来试试这款MiXplorer。 MiXplorer是一款非常强大实用的手机文档管理器,能给用户提供了一系列的文档处理功能,包括本地文件浏览、文件排序、文件筛选、切换视图、新建文件、添

SSH免密登录配置

情况 服务器A: 192.168.0.101 服务器B: 192.168.0.102 在服务器A上 可以使用 ssh root192.168.0.102 无需密码登录到192.168.0.102 配置 服务器A: 192.168.0.101 上查看是否有ssh公钥 1.进入.ssh目录&#xff1a; cd ~/.ssh 2.找到id_rsa.pub文件&#xff1a; ls 3.查看公钥…

ucos3+emwin+appwizard控制硬件LED

1.新建appwizard项目2.AppWizard 设计器导出代码&#xff0c;点击 File→Export&Save 3.移植代码到keil其中&#xff0c;Soure 和Resource 中的C文件&#xff0c;添加到项目代码Simulation和Soure 和Resource 中的h文件,添加到编译器的include path。修改Source\Generated下…