当前位置: 首页 > 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; 这里…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...