Android Jetpack架构组件 ViewModel详解
作者:黄林晴 发布时间:2021-09-08 00:29:20
前言
前面两篇文章我们已经学习了Lifecycle和DataBind,本篇文章我们来学习Jetpack系列中比较重要的ViewModel,Jetpack的很多很多组件都是搭配使用的,所以单独的知识点可能会有些”无意义“但却是我们项目实战的基础!
ViewModel的使用
ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续存在。这句话很好理解,还记得我们在讲解Lifecycle的时候 举的例子吗,我们还是使用那个例子,如果你还没看过,可移步至:
Android Jetpack系列之Lifecycle
我们再回顾一次需求:
在Activity 可见的时候,我们去做一个计数功能,每隔一秒 将计数加1 ,当Activity不可见的时候停止计数,当Activity被销毁的时候 将计数置为0,这里我们在Activity被销毁的时候不再将count置为0,WorkUtil代码如下所示:
public class WorkUtil implements LifecycleObserver {
private static final String TAG = "WorkUtil";
private boolean whetherToCount = true;
private int count = 0;
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void start() {
new Thread(new Runnable() {
@Override
public void run() {
while (whetherToCount) {
try {
Thread.sleep(1000);
count++;
Log.d(TAG, "start: " + count);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
whetherToCount = false;
Log.d(TAG, "onStop: ");
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestory() {
}
}
我们运行程序,在计数的过程中 使屏幕旋转,运行结果如下所示:
我们可以看到,当屏幕旋转的时候,由于生命周期发生了改变,导致数据被销毁,所以计数器的计数又从初始值开始计数了,那么我们如何解决这个问题呢,你肯定会说,缓存呀,重写onSabeInstanceState()方法等等,都可以但是都不够优雅,那么如何优雅的来解决这个问题呢,这就是我们的今天的主角 ViewModel。
ViewModel的使用
我们新建Main3ActivityModel 继承自 ViewModel,在Main3ActivityModel中定义count变量 如下所示:
public class Main3ActivityViewModel extends ViewModel {
public int count = 0;
}
没错,就是这么简单,我们只要保证计数的变量是这个model中的变量,就可以解决我们上面的问题
我们通过ViewModelProviders来获取ViewModel对象
main3ActivityViewModel = ViewModelProviders.of(this).get(Main3ActivityViewModel.class);
但是这个方法已经过时了,替代方法是
main3ActivityViewModel = new ViewModelProvider(this).get(Main3ActivityViewModel.class);
为了让WorkUtil使用Model中的变量,所以我们要将ViewModel 传递过去,在WorkUtil中新增一个构造方法
private Main3ActivityViewModel main3ActivityViewModel;
public WorkUtil(Main3ActivityViewModel main3ActivityViewModel) {
this.main3ActivityViewModel = main3ActivityViewModel;
}
我们将WorkUtil中的计数变量count 改为 main3ActivityViewModel.count,如下所示:
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void start() {
new Thread(new Runnable() {
@Override
public void run() {
while (whetherToCount) {
try {
Thread.sleep(1000);
main3ActivityViewModel.count++;
Log.d(TAG, "start: " + main3ActivityViewModel.count);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
main3Activity中在lifecycle中传参:
getLifecycle().addObserver(new WorkUtil(main3ActivityViewModel));
再次运行程序,运行过程中旋转手机屏幕,打印如下所示:
我们可以看到,在屏幕旋转之后,计数器的计数保留了,那么viewModel是如何做到的呢,这是因为ViewModel 对象存在的时间比视图或 LifecycleOwners 的特定实例存在的时间更长,ViewModel的生命周期如下图所示(摘自官网)
向ViewModel传参
当前计数的需求是从0开始计时,我们现在修改需求如下,使用用户输入的数字为起点开始计数,这样的话ViewModel中的count就不是0了,而是传入的参数,我们在Main3Activity中定义变量inputCount 来模拟用户输入的数字
private int inputCount = 100;
在Main3ViewModel中添加构造方法
public int count = 0;
public Main3ActivityViewModel(int count) {
this.count = count;
}
看到这里,你可能会说,我们直接new一个传过去不就行了吗,请记住这是万万不行的,因为如果我们使用直接实例化来创建ViewModel,那么ViewModel的生命周期就受Activity的影响了,所以为什么我们只能通过ViewModelProvider来获取ViewModel的实例。
我们需要借助ViewModelProvider.Factory来实现传参,新建Main3ActivityViewModelFactor继承自 ViewModelProvider.Factory,重写其onCreate方法,如下所示:
public class Main3ActivityViewModelFactory implements ViewModelProvider.Factory {
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return null;
}
}
添加一个构造方法,并在create中创建VideModel实例
private int count;
public Main3ActivityViewModelFactory(int count) {
this.count = count;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new Main3ActivityViewModel(count);
}
在Activity中获取实例的时候 采用如下方法
main3ActivityViewModel = new ViewModelProvider(this,new Main3ActivityViewModelFactory(inputCount)).get(Main3ActivityViewModel.class);
如此一来 我们就实现ViewModel传递参数了~
来源:https://blog.csdn.net/huangliniqng/article/details/106218260


猜你喜欢
- 今天看到,java的NIO里面的SelectionKey,处理完key后,调用keyIterator.remove(); 对Iterator
- 一款书籍阅读器,需要以下功能才能说的上比较完整:文字页面展示,即书页;页面之间的跳转动画,即翻页动作;能够在每一页上记录阅读进度,即书签;能
- 这篇文章主要介绍了Springboot分页插件使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要
- 在java开发中,类、接口、方法,都需要进行注释,注释内容如图:注释中的基本元素有:描述、作者、创建日期。可增加元素有:修改日期、修改内容、
- 到底什么是反射呢???反射的核心就是JVM在运行时才动态加载类或调用方法,访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。每
- 面试题:1同步方法和同步块,哪种更好?2.如果同步块内的线程抛出异常会发生什么?1. 同步方法和同步块,哪种更好?同步块更好,这意味着同步块
- 前言在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,例如使用devtool来实现
- 在 Unity 中实现对游戏数据存储和读取的方法主要有这几种:使用本地持久化类 PlayerPrefs使用二进制的方法序列化和反序
- Struts提供了一个更简单的方式来处理未捕获的异常,并将用户重定向到一个专门的错误页面。您可以轻松地Struts配置到不同的异常有不同的错
- 我们公司做了一款使用百度钱包的移动网页支付进行支付的产品,用户通过百度钱包、百度糯米扫描我们产品的二维码,选择商品,点击支付将会自动调用百度
- 前言如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,
- Android setButtonDrawable()的兼容问题解决办法setButtonDrawable()的兼容问题API1
- 背景在接口请求过程中,传递json对象,springboot转换为实体VO对象后,所有属性都为null。post请求:后台接收请求:当时就懵
- 函数名称 说明ActiveKeyboardLayout 激活一个不同的键盘布局,该布局必须先由 LoadKeyBoardLayout函数装载
- 在之前的博客使用SpringMVC创建Web工程并使用SpringSecurity进行权限控制的详细配置方法 中,我们描述了如何配置一个基于
- 一、系统介绍本系统实现的以下功能管理员功能:登录系统、病人信息的增删改查、就医档案的录入、医生信息的增删改查、科室信息的增删改查、收费统计功
- 本文实例讲述了Android编程使用Service实现Notification定时发送功能。分享给大家供大家参考,具体如下:/** * 通过
- 一、问题描述在C#中is,as,using关键字具有其特点及使用场景,其中is关键字用于检查该对象是否与给定类型兼容,as关键字用于将对象转
- 目录1 任务状态手动控制任务启动确保任务已激活2 任务取消3 进度报告4 Task.Yield 让步5 定制异步任务后续操作Configur
- 先上图下拉刷新跟原生开发一样,下拉刷新在flutter里提供的有组件实现 RefreshIndicator一直不明白为啥组件中都提供下拉刷新