JetPack组件学习ViewModel

news2024/11/24 4:24:33

ViewModel的使用

1.需要先创建ViewModel类,继承自ViewModel重写onclear方法,使得页面销毁的时候能够走到自定义的onClear方法中

class MyViewModel : ViewModel() {
    //共享数据的核心在于拿到同一个LiveData实例,也就是拿到同一个ViewModel实例,其保存在ViewModelStore中
    //而ViewModelStore是Activity/Fragment提供的(做了屏幕转换的恢复处理,ViewModelStore会保存其数据)
    var progress:MutableLiveData<Int>?=null
    override fun onCleared() {
        //页面销毁回调
        super.onCleared()
        Log.i("wwwwwwwwwwwwwwwww", "onCleared:MyVBiewModel    cleared ")
    }
}

2 创建ViewModelProvider 在Activity中创建ViewModelProvider实例需要ViewModelOwner作为参数

和LifeCyclerOwner一样都是CommpentActivity实现的接口

除此之外还需要一个工厂

该工厂默认实现是获取get函数传入的class反射创建ViewModel实例;也可以自定义工厂函数,会接受一个class的参数只需要返回该实例即可,中间的操作可以自定义

一,传入ViewModelOwner,Activity/Fragment已经实现该接口
ViewModelProvider(this)
.get(MyViewModel::class.java) //默认实现反射创建ViewModel实例

二,创建实例过程自定义返回ViewModel实例即可
ViewModelProvider(this,object :ViewModelProvider.Factory{
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                modelClass.constructors
            return    modelClass.getConstructor(Application::class.java).newInstance(application)
            }
        }).get(RoomViewModel::class.java).

上面是利用反射创建了一个带有参数的ViewModel。默认创建的是无参的实例

3.通过get传入对应的Viewmodel的Class对象即可。

简要分析

首先创建ViewModelProvider实例,看下对应源码:

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
		//第一个参数调用其getViewModelStore函数
		//第二个参数是个工厂稍后分析
        this(owner.getViewModelStore(), factory);
    }

可以看到Fragment和ComponentActivity都实现了该接口

在这里插入图片描述

接下来查看ComponentActivity。 1.首次mViewModelStore肯定为null,从nc中取出肯定也取不到只能通过new的方式去创建 2.当经历了屏幕旋转这时候就会从configure中取出viewmodelStore了,对应的也就是第二个红框,最后说明这个流程。

在这里插入图片描述

创建完ViewModelProvider后,调用get方法获取Viewmodel实例。

private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";

 public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        //传入两个参数,
        //第一个是之后做缓存用的  DEFAULT_KEY 是常量
        //第二个是自定义ViewModel的class
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    	//首先从缓存中获取  viewmodelStore可以看成是一个map,保存ViewModel
        ViewModel viewModel = mViewModelStore.get(key);
			
		//可以看到mFactory 分为两类:
		1.OnRequeryFactory 当缓存命中后该方法会回调并将命中的viewmodel传入
		2.KeyedFactory 继承自OnRequeryFactory 并提供create函数提供class创建实例过程有用户自定义
        if (modelClass.isInstance(viewModel)) {
        	//OnRequeryFactory是缓存命中后的回调
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
        	
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //调用create函数将class传入,内部使用不同的构造方法创建实例并返回
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        //缓存该ViewModel
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

ViewModelStore源码:

public class ViewModelStore {
//可以看到就是个map缓存
    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();
    }
}

问答

如何实现旋转屏幕数据保持不变?

答:1.第一次创建 首先会从对应的Activity中的NoLastConfigure获取activity取出对应的ViewModelStore这个时候由于是第一次所以是null, 于是只能new一个ViewModelStore。

2.Activity重建时会销毁页面将ViewmodelStore保存到lastCongiure中并保存到ActivityClientRecord中传递给AMS端。

3.AMS重新调用(这里需要注意如果是配置引起的重建会走RelauchActivity而不是第一次普通的lauchActivity)ReLaunch会通过token取出对应的AcRecord,在attach的时候将record中上一次保存的lastCoinfigure取出来

4.onCreate的时候把store赋值给NoLastConfigure,这个时候页面执行onCreate获取 ViewModelStore就可以获取到了,而且Store是保存着这个页面的所有Viewmodel所以上一次的ViewModel中的数据还在并没有销毁

详细流程: HandlerRelauncherActivity中先调用handlerdestory销毁页面保存重要配置到record中(AMS会保存token{Activity唯一标识}和record的map),在调用lauchActivity重建页面通过token重新取出record,record在取出configure保存到新创建的activity的属性中。

1.当调用performDestory的时候创建一个Configure类取出viewmodelStore中如果没有直接取到从上一次的configure中取,创建完configure后保存到record中的lastConfigure属性中。

2.何时重建:在performLaunchActivity方法中调用attach方法,在这个方法中取出record中的lastConfigure赋值给成员变量mLastConfigure

和之前的Presenter有什么区别

个人感觉:

1.持有V层引用这个很好地解决了,但是回调V层还是得利用很多接口进行传递数据,这种主动通知V层的方式虽然变成接口回调的方式本质上耦合还是严重,可以通过LiveData V层去观察ViewModel中的数据变化这样耦合会降低一些

2.当配置失效比如屏幕旋转会销毁重建Activity,数据虽说可以通过onSavedInstance来传递,但是数据量并不能太大。但是ViewModel是系统原生支持的我们可以直接获取到上次销毁的ViewModel实例数据还在其中

3.具有生命周期可以自动管理防止泄漏,可通过onCleared告知持有该ViewModel的V层销毁

4.缓存命中和创建Viewodel都有回调,可以做自定义处理

原文链接:JetPack组件学习ViewModel - 掘金 (juejin.cn)

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

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

相关文章

面试算法102:加减的目标值

题目 给定一个非空的正整数数组和一个目标值S&#xff0c;如果为每个数字添加“”或“-”运算符&#xff0c;请计算有多少种方法可以使这些整数的计算结果为S。例如&#xff0c;如果输入数组[2&#xff0c;2&#xff0c;2]并且S等于2&#xff0c;有3种添加“”或“-”的方法使…

商中在线(商务中国)域名外部入库流程

注册商是商中在线&#xff0c;且在商中在线管理的&#xff0c;请使用此教程外部入库。 如您的域名注册商是商中在线但在聚名管理&#xff0c;请参考教程&#xff1a;聚名平台域名外部入库流程 -西部数码帮助中心 一、在我司提交入库 1、在【业务管理】-【域名管理】-【外…

Qt/QML编程学习之心得:一个音频播放器的实现(29)

在window下&#xff0c;打开音乐播放器&#xff0c;然后打开一个.mp3文件&#xff0c;就可以实现播放了&#xff0c;那么在Qt/QML中如何实现呢&#xff1f;首先所有的设计都是基于音乐播放器的&#xff0c;嵌入式linux下同样也有音乐播放器&#xff0c;比如mplayer。其调用方法…

2_工厂设计_工厂方法和抽象工厂

工厂设计模式-工厂方法 1.概念 工厂方法模式(Fatory Method Pattern ) 是指定义一个创建对象的接口&#xff0c;但让实现这个接口的类来决定实例化哪个类&#xff0c;工厂方法让类的实例化推迟到子类中进行。 在工厂方法模式中用户只需要关心所需产品对应的工厂&#xff0c;…

阿里开源AnyText:可在图像中生成任意精准文本,支持中文!

‍随着Midjourney、Stable Difusion等产品的出现&#xff0c;文生图像领域获得了巨大突破。但是想在图像中生成/嵌入精准的文本却比较困难。 经常会出现模糊、莫名其妙或错误的文本&#xff0c;尤其是对中文支持非常差&#xff0c;例如&#xff0c;生成一张印有“2024龙年吉祥…

数据分析基础之《pandas(1)—pandas介绍》

一、pandas介绍 1、2008年Wes McKinney&#xff08;韦斯麦金尼&#xff09;开发出的库 2、专门用于数据分析的开源python库 3、以numpy为基础&#xff0c;借力numpy模块在计算方面性能高的优势 4、基于matplotlib能够简便的画图 5、独特的数据结构 6、也是三个单词组合而…

2024年数学建模美赛能用chatGPT之类的AI吗?官方给了明确规定!

这两年chatGPT等大语言模型火了&#xff0c;能对话&#xff0c;自然也能回答数学建模方面的问题。 那美赛能不能用这些AI呢&#xff1f;2024年美赛官方对chatGPT等的使用做出了明确的规定&#xff08;其中的VI. Contest Instructions部分&#xff09;&#xff1a; https://ww…

《2024 AIGC 应用层十大趋势白皮书》:近屿智能OJAC带您一起探索AI未来

Look&#xff01;&#x1f440;我们的大模型商业化落地产品&#x1f4d6;更多AI资讯请&#x1f449;&#x1f3fe;关注Free三天集训营助教在线为您火热答疑&#x1f469;&#x1f3fc;‍&#x1f3eb; 近日国际知名咨询机构IDC发布《2024 AIGC 应用层十大趋势白皮书》的发布&am…

水文模型(科普类)

SWMM 模型概况&#xff1a; SWMM5 系列拥有编辑区域数据的功能&#xff0c;而且能模拟水文、 水力和水质。其核心部分是管道汇流计算模块&#xff0c;提供了恒定流法、运动波法和动力波法三种水动力学 方法。其中动力波法通过求解完整的圣维南方 程组进行计算&#xff0c;能够…

WPF自定义漂亮顶部工具栏 WPF自定义精致最大化关闭工具栏 wpf导航栏自定义 WPF快速开发工具栏

在WPF应用程序开发中&#xff0c;自定义一个漂亮的顶部工具栏具有多重关键作用&#xff0c;它不仅增强了用户体验&#xff0c;还提升了整体应用的专业性和易用性。以下是对这一功能的详细介绍&#xff1a; 首先&#xff0c;自定义顶部工具栏是用户界面设计的重要组成部分&…

508基于51单片机的火灾检测与报警系统设计

基于51单片机的火灾检测与报警系统设计[proteus仿真] 火灾检测与报警系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的火灾检测与报警系统设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 …

BigDecimal的性能问题

BigDecimal 是 Java 中用于精确计算的数字类&#xff0c;它可以处理任意精度的小数运算。由于其精确性和灵活性&#xff0c;BigDecimal 在某些场景下可能会带来性能问题。 BigDecimal的性能问题 BigDecimal的性能问题主要源于以下几点&#xff1a; 内存占用&#xff1a;BigDec…

华为OD机试 - 反射计数(Java JS Python C)

题目描述 给定一个包含 0 和 1 的二维矩阵。 给定一个初始位置和速度,一个物体从给定的初始位置出发,在给定的速度下进行移动,遇到矩阵的边缘则发生镜面发射。 无论物体经过 0 还是 1,都不影响其速度。 请计算并给出经过 t 时间单位后,物体经过 1 点的次数。 矩阵以左…

国产编程语言炫彩,界面库ui dll,有人了解吗

中文编程: 中英文双语编程, 中英一键切换, 中英对照, 中文为主, UNICODE/ANSI编码都支持; 完全免费: 炫语言免费, 调试器免费, IDE绿色版无需安装; 纯文本: 纯文本格式代码, 随意复制粘贴, GIT代码托管, 多人合作开发; PY风格: PY风格代码, 通过代码缩进确定作用域 非 大花括…

B+树索引及其原理

MySQL索引的底层结构是B树&#xff0c;为什么它会选择这个结构&#xff1f;联合索引是怎么实现的&#xff1f;最左侧匹配原则的原理是什么&#xff1f;本文将一一解答这些疑惑。 1 前置知识 在学习B树之前&#xff0c;我们先了解下其他的树形结构&#xff1a;二叉树、平衡二叉…

C++中的返回值优化(RVO)

一、命名返回值优化&#xff08;NRVO&#xff09; 是Visual C2005及之后版本支持的优化。 具体来说&#xff0c;就是一个函数的返回值如果是一个对象。那么&#xff0c;正常的返回语句的执行过程是&#xff0c;把这个对象从当前函数的局部作用域&#xff0c;或者叫当前函数的…

vue+element弹窗内---下拉框定位问题解决(方法之一)

问题: 加了 :popper-append-to-body"false" 这个属性也不好用时 可以试试这个 解决: 第一步: 找到el-select标签添加(popper-class"popperClass")属性-----如下图 第二步:在css中添加如下代码即可 ::v-deep .popperClass{ top:auto !important; }

Java学习苦旅(二十二)——MapSet

本篇博客将详细讲解Map和Set。 文章目录 搜索概念模型 MapMap.Entry<K, V>Map的常用方法说明TreeMap和HashMap的区别 Set常用方法说明TreeSet和HashSet的区别 结尾 搜索 概念 Map和set是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例…

【win11 绕过TPM CPU硬件限制安装】

Qt编程指南 VX&#xff1a;hao541022348 ■ 下载iso文件■ 右键文件点击装载出现如下问题■ 绕过TPM CPU硬件限制安装方法■ 虚拟机安装win11 ■ 下载iso文件 选择Windows11 &#xff08;multi-edition ISO&#xff09;在选择中文 ■ 右键文件点击装载出现如下问题 ■ 绕过T…