当前位置: 首页 > news >正文

Jetpack之ViewModel

The ViewModel class is a business logic or screen level state holder.

上面是官方给的定义,ViewModel 类是业务逻辑屏幕级状态持有者。

一、业务逻辑持有者

在此之前,无论是MVC模式,还是MVP模式,在视图层,都会产生对业务逻辑层的依赖,从而导致出现内存泄漏的情况。那ViewModel又是如何处理业务逻辑层和视图层的依赖,从而避免了常见的内存泄漏的问题的呢?
在这里插入图片描述

依赖Lifecycle对宿主生命周期的感知,处理对Viewmodel的依赖

1.1、应用

NormalViewModel

package com.anniljing.viewmodelcorestudy;import android.app.Application;import java.util.ArrayList;
import java.util.List;import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;public class NormalViewModel extends AndroidViewModel {public NormalViewModel(@NonNull Application application) {super(application);}public List<Integer> getdatas() {List<Integer> data = new ArrayList<>();for (int i = 0; i < 10; i++) {data.add(i);}return data;}
}

NormalActivity

package com.anniljing.viewmodelcorestudy;import android.os.Bundle;import com.anniljing.viewmodelcorestudy.databinding.ActivityNormalBinding;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;public class NormalActivity extends AppCompatActivity {private ActivityNormalBinding mBinding;private NormalViewModel mViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mBinding = ActivityNormalBinding.inflate(getLayoutInflater());setContentView(mBinding.getRoot());mViewModel = new ViewModelProvider(this).get(NormalViewModel.class);}
}

ComponentActivity源码

          getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_DESTROY) {// Clear out the available contextmContextAwareHelper.clearAvailableContext();// And clear the ViewModelStoreif (!isChangingConfigurations()) {getViewModelStore().clear();}}}});

此处在Activity中Lifecycle的ON_DESTROY事件中,清除了ViewModel。

1.2、源码解析

在这里插入图片描述

1.2.1 创建ViewModelStore

public ComponentActivity() {Lifecycle lifecycle = getLifecycle();//noinspection ConstantConditionsif (lifecycle == null) {throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "+ "constructor. Please make sure you are lazily constructing your Lifecycle "+ "in the first call to getLifecycle() rather than relying on field "+ "initialization.");}getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_DESTROY) {// Clear out the available contextmContextAwareHelper.clearAvailableContext();// And clear the ViewModelStoreif (!isChangingConfigurations()) {getViewModelStore().clear();}}}});getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {ensureViewModelStore();getLifecycle().removeObserver(this);}});}

在ComponentActivity的构造方法中,注册了多个Lifecycle的观察者,其中有两个涉及ViewModelStore的,一个是收到宿主的生命周期事件后,就创建了ViewModelStore,随后就移除了该观察者;另一个则是监听到宿主的ON_DESTROY事件后,清空ViewModelStore的ViewModel

1.2.2、创建Factory

1.2.2.1、创建ViewModel的时候,第一步我们创建了ViewModelProvider,调用了其中一个构造方法

 public constructor(owner: ViewModelStoreOwner) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

1.2.2.2、ViewModelProvider的构造方法中,调用了默认的Factory

public companion object {internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =if (owner is HasDefaultViewModelProviderFactory)owner.defaultViewModelProviderFactory else instanceinternal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"}

1.2.2.3、由于ComponentActivity实现了HasDefaultViewModelProviderFactory接口,所以Factory的实际对象是SavedStateViewModelFactory

  @NonNull@Overridepublic ViewModelProvider.Factory getDefaultViewModelProviderFactory() {if (mDefaultFactory == null) {mDefaultFactory = new SavedStateViewModelFactory(getApplication(),this,getIntent() != null ? getIntent().getExtras() : null);}return mDefaultFactory;}

1.2.3、创建ViewModel

public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {val viewModel = store[key]if (modelClass.isInstance(viewModel)) {(factory as? OnRequeryFactory)?.onRequery(viewModel)return viewModel as T} else {@Suppress("ControlFlowWithEmptyBody")if (viewModel != null) {// TODO: log a warning.}}val extras = MutableCreationExtras(defaultCreationExtras)extras[VIEW_MODEL_KEY] = key// AGP has some desugaring issues associated with compileOnly dependencies so we need to// fall back to the other create method to keep from crashing.return try {factory.create(modelClass, extras)} catch (e: AbstractMethodError) {factory.create(modelClass)}.also { store.put(key, it) }}

应用层通过ViewModelProvider的get方法创建ViewModel,并且保存到ViewModelStore中。

二、屏幕状态的持有者

该功能主要处理当设备配置发生变化时,界面会重新绘制,导致数据丢失的问题。
在这里插入图片描述
屏幕发生旋转的时候,会调用onDestroy方法,界面会重新绘制。

2.1、ViewModel之前

2.1.1、manifest中配置

在这里插入图片描述
在activity里面的configChanges属性里面配置orientation|screenSize,则屏幕旋转的时候,界面就不会重新绘制。

2.1.2、onSaveInstanceState和onRestoreInstanceState

如果manifest里面没有配置configChanges,则屏幕旋转的时候,在onDestroy之前会调用onSaveInstanceState,在恢复界面的时候,会调用onRestoreInstanceState,这样我们就可以在以上两个方法中做保存和恢复数据的逻辑。

package com.anniljing.viewmodelcorestudy;import android.os.Bundle;
import android.util.Log;import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;public class SaveStateActivity extends AppCompatActivity {private static final String TAG = "SaveStateActivity";private ActivitySaveStateBinding mBinding;private NormalState mNormalState;private static final String KEY="NormalState";private String saveString="";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.e(TAG, "onCreate");if (savedInstanceState !=null){saveString=savedInstanceState.getString(KEY,"");}mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());setContentView(mBinding.getRoot());mNormalState = new NormalState();mNormalState.setString(saveString);Log.e(TAG, mNormalState.getString());mBinding.tvState.setOnClickListener((view) -> {mNormalState.setString("NormalState");Log.e(TAG,"Click after:"+ mNormalState.getString());});}@Overrideprotected void onSaveInstanceState(@NonNull Bundle outState) {super.onSaveInstanceState(outState);Log.e(TAG, "onSaveInstanceState");outState.putString(KEY,"NormalState");}@Overrideprotected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);Log.e(TAG, "onRestoreInstanceState");}@Overrideprotected void onDestroy() {super.onDestroy();Log.e(TAG, "onDestroy");}
}

保存数据:
在这里插入图片描述
重新绘制的时候,取出数据:
在这里插入图片描述
在这里插入图片描述
在onSaveInstanceState里面增加保存逻辑,在onCreate方法里面就可以恢复以前的数据了。

2.2、 使用ViewModel

ViewModelSaveState

package com.anniljing.viewmodelcorestudy;import android.app.Application;import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;public class ViewModelSaveState extends AndroidViewModel {private String mString;public ViewModelSaveState(@NonNull Application application) {super(application);}public String getString() {return mString;}public void setString(String string) {mString = string;}
}
package com.anniljing.viewmodelcorestudy;import android.os.Bundle;
import android.util.Log;import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;public class SaveStateActivity extends AppCompatActivity {private static final String TAG = "SaveStateActivity";private ActivitySaveStateBinding mBinding;private ViewModelSaveState mViewModelSaveState;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.e(TAG, "onCreate");mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());setContentView(mBinding.getRoot());mViewModelSaveState = new ViewModelProvider(this).get(ViewModelSaveState.class);Log.e(TAG,"Before click:"+mViewModelSaveState.getString());mBinding.tvState.setOnClickListener((view) -> {mViewModelSaveState.setString("ViewModelState");Log.e(TAG, "Click after:" + mViewModelSaveState.getString());});}@Overrideprotected void onDestroy() {super.onDestroy();Log.e(TAG,"onDestroy");}
}

在这里插入图片描述
可以看到,引入ViewModel以后,不需要在onSaveInstanceState方法中增加额外的保存逻辑,重绘后就可以恢复以前的数据。

无论是ViewModel,还是重写onSaveInstanceState方法,当内存有限导致界面退出重绘时,就无法恢复以前的数据了。
我们可以通过以下操作来模拟内存不足时,导致应用杀死的效果:
1、启用开发者模式
2、打开“不保留活动”
3、打开应用后,按home键,退出应用
4、重新打开应用。
在这里插入图片描述

2.3、使用SavedStateHandle

ViewModelWithHandleSaveState

package com.anniljing.viewmodelcorestudy;import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;public class ViewModelWithHandleSaveState extends ViewModel {private static final String KEY = "ViewModelWithHandleSaveState";private SavedStateHandle mStateHandle;public ViewModelWithHandleSaveState(SavedStateHandle stateHandle) {mStateHandle = stateHandle;}public MutableLiveData<String> getLiveData() {return mStateHandle.getLiveData(KEY);}
}

1、构造方法里面添加SavedStateHandle参数。
2、获取的时候通过mStateHandle。
SaveStateActivity

package com.anniljing.viewmodelcorestudy;import android.os.Bundle;
import android.util.Log;import com.anniljing.viewmodelcorestudy.databinding.ActivitySaveStateBinding;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;public class SaveStateActivity extends AppCompatActivity {private static final String TAG = "SaveStateActivity";private ActivitySaveStateBinding mBinding;private ViewModelWithHandleSaveState mWithHandleSaveState;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.e(TAG, "onCreate");mBinding = ActivitySaveStateBinding.inflate(getLayoutInflater());setContentView(mBinding.getRoot());mWithHandleSaveState = new ViewModelProvider(this).get(ViewModelWithHandleSaveState.class);Log.e(TAG, "Before click:" + mWithHandleSaveState.getLiveData().getValue());mWithHandleSaveState.getLiveData().observe(this, s -> {Log.e(TAG, "After click:" + mWithHandleSaveState.getLiveData().getValue());});mBinding.tvState.setOnClickListener((view) -> mWithHandleSaveState.getLiveData().postValue("ViewModelWithHandleSaveState"));}@Overrideprotected void onDestroy() {super.onDestroy();Log.e(TAG, "onDestroy");}
}

在这里插入图片描述

相关文章:

Jetpack之ViewModel

The ViewModel class is a business logic or screen level state holder. 上面是官方给的定义&#xff0c;ViewModel 类是业务逻辑或屏幕级状态持有者。 一、业务逻辑持有者 在此之前&#xff0c;无论是MVC模式&#xff0c;还是MVP模式&#xff0c;在视图层&#xff0c;都会…...

追梦之旅【数据结构篇】——详解C语言动态实现顺序表

详解C语言动态实现顺序表~&#x1f60e;前言&#x1f64c;顺序表概念及结构&#x1f64c;功能函数的具体实现分析&#xff1a;&#x1f64c;尾插函数具体实现&#xff1a;尾删函数具体实现&#xff1a;头插函数具体实现&#xff1a;头删插函数具体实现&#xff1a;任意插函数具…...

xss基础

目录标题一、XSS的原理二、XSS漏洞分类1、反射型xss2、存储型XSS3、基于DOM的XSS三、XSS漏洞的危害及验证四、XSS漏洞的黑盒测试五、XSS漏洞的白盒测试一、XSS的原理 跨站脚本攻击XSS&#xff08;Cross Site Scripting&#xff09;&#xff0c;为了不和层叠样式表&#xff08;…...

移动WEB开发二、流式布局

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…...

分享在线预约系统制作步骤_在线预约链接怎么做

在微信小程序上进行在线预约&#xff0c;不管是商家还是顾客&#xff0c;都可以自由选择时间&#xff0c;顾客还可以通过预约小程序&#xff0c;了解到所选服务的详情和功能特色&#xff0c;不必等到去店内听介绍&#xff0c;顾客能节省等候时间&#xff0c;商家能解放招待人力…...

【每日一题Day125】LC1326灌溉花园的最少水龙头数目 | 动态规划 贪心

灌溉花园的最少水龙头数目【LC1326】 在 x 轴上有一个一维的花园。花园长度为 n&#xff0c;从点 0 开始&#xff0c;到点 n 结束。 花园里总共有 n 1 个水龙头&#xff0c;分别位于 [0, 1, ..., n] 。 给你一个整数 n 和一个长度为 n 1 的整数数组 ranges &#xff0c;其中 …...

C# FFmpeg推流Vlc.DotNet拉流优化参数

FFmpeg是流媒体开源神器&#xff0c;视频转换、剪裁包括推流&#xff0c;无所不能&#xff0c;很多系统都是基于其开发的。拉流可以用FFplay&#xff0c;但是不利于集成到自己的代码中&#xff0c;因此拉流选择了Vlc.DotNet。 在使用中&#xff0c;仅使用默认参数&#xff0c;…...

pnpm v8版本升级变化关注点(前瞻速攻版)

前言 pnpm v8.0.0-alpha.0 版本已经发布&#xff0c;包含少量变化&#xff0c;但其中还是有令人在意的点的。 本文将默认读者拥有大部分 pnpm v7 版本的知识储备&#xff0c;进行 v8 版本的前瞻速攻。 安装方法 目前通过指定 Tag 方式可以安装 v8 alpha 版&#xff1a; npm…...

Python基础-环境安装

Python安装1.下载PythonPython网址&#xff1a;https://www.python.org/进入Python官网&#xff0c;点击Downloads&#xff0c;选择自己对应的操作系统&#xff08;此处以Windows为例&#xff09;在左侧的稳定发行版中&#xff0c;选择一个3.5版本以上的&#xff0c;然后点击对…...

重载、重写、重构概念辨析

首先&#xff0c;重载、重写、重构都表现为方法名相同 重载 重载&#xff08;overload&#xff09;&#xff0c;表示同一类的方法之间的关系&#xff0c;至少有以下其中一种情况 参数个数不同参数类型不同参数顺序不同 注意&#xff0c;返回值类型不同不能作为重载依据 重…...

第九章 - 多表查询(join,left join 等)与合并查询(union union all)

第九章 - 多表查询&#xff08;join&#xff0c;left join 等&#xff09;与合并查询&#xff08;union&#xff09;交叉链接&#xff08;笛卡尔积&#xff09;内连接查询外连接查询左链接&#xff1a; left join右链接&#xff1a;right join组合查询 union & union all使…...

matplotlib学习笔记(持续更新中…)

目录 1. 安装&#xff0c;导入 2. figure&#xff0c;axes&#xff08;图形&#xff0c;坐标图形&#xff09; 2.1 figure对象 2.2 axes对象 2.3 代码演示 2.3 subplot() 方法 3. 图表的导出 3.1 savefig() 方法 3.2 代码演示 1. 安装&#xff0c;导入 pip install m…...

STM32 SystemInit()函数学习总结

拿到程序后如何看系统时钟&#xff1f;User文件夹——system_stm32f4xx程序&#xff0c;先找systemcoreclock(系统时钟&#xff09;但是这里这么多个系统时钟应该如何选择?点击魔法棒&#xff0c;然后点击C/C可以看到define的是F40_41XXX.USE这一款 &#xff0c;对应着就找出了…...

【Spring Boot 原理分析】- 自动配置

【Spring Boot 原理分析】- 自动配置 Condition 注解 Condition 是 Spring 4.0 增加的条件判断功能&#xff0c;通过这个功能可以实现选择的创建 Bean 操作 &#x1f451; 我们在使用 Spring 的时候&#xff0c;只需导入某个依赖的坐标&#xff0c;就可以直接通过 Autwired 注…...

简明易懂的JVM理解

文章目录简明易懂的JVM和GC理解写在前面Java虚拟机(JVM)的组成基本介绍结构类加载子系统(ClassLoader SubSystem)介绍类加载过程类加载过程小结双亲委派模型(Parent-Delegation Model)简介优点Java9的类加载的委派关系变动双亲委派模型小结运行时数据区(Runtime Data Areas)介绍…...

新考纲下的PMP考试有多难?

PMP考试在6月25号考试结束后&#xff0c;在网上引起一片哗然&#xff0c;新考纲领域与考点的转变使得考试难度加大&#xff1a;PMP考试敏捷和混合内容比重大&#xff0c;考试难度加大很多&#xff1b;考题更加注重考生的知识应用能力&#xff0c;领域更宽&#xff1b; 接下来我…...

朗润国际期货:知名投行/大佬打Call记

知名投行/大佬打Call记 2023年知名投行/大佬看好哪些投资标的 中国股市 高盛&#xff08;2023年1月&#xff09;&#xff1a;将上涨15% 花旗&#xff08;2023年1月&#xff09;&#xff1a;上半年会成为投资两点 摩根大通&#xff08;2022年11月&#xff09;&#xff1a;M…...

遗传算法及Python实现

0 建议学时 4学时 1 人工智能概述 2020中国人工智能产业年会在苏州召开&#xff0c;会上发布的《中国人工智能发展报告2020》显示&#xff0c;过去十年(2011-2020) &#xff0c;中国人工智能专利申请量达389571件&#xff0c;占全球总量的74.7%&#xff0c;位居世界第一。 报…...

零基础 Ubuntu 20.04.01 下搭建51单片机开发环境[开源编译器SDCC]

原创首发于CSDN&#xff0c;转载请注明出处&#xff0c;谢谢&#xff01; 文章目录为何会在Linux下开发单片机个人系统环境与所用开发板安装开源编译器 sdccSTC MCU ISP 闪存工具 stcgal 的安装单片机代码的编译与测试&#xff5c;编写主代码 main.c&#xff5c;使用 sdcc 编译…...

手摸手快速入门 正则表达式 (Vue源码中的使用)

vue2源码 在 vue2 源码的 src\compiler\parser\html-parser.js 文件中 里面有大量的正则表达式&#xff0c;如下图 可以看到非常的长&#xff0c;不是我说&#xff0c;就前几行&#xff0c;如果没有相关的 正则表达式 的工具&#xff0c;我可能就被劝退了&#x1f62d; 这里…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...