ViewModel和LiveData问题思考与解答
嗨,大家好,面试真题系列又来了,今天我们说说MVVM架构里的两大组件:ViewModel和LiveData。
还是老样子,提出问题,做出解答。
ViewModel 是什么?
ViewModel 为什么被设计出来,解决了什么问题?
说说ViewModel原理。
LiveData 是什么?
LiveData 为什么被设计出来,解决了什么问题?
说说LiveData原理。
ViewModel 是什么,说说你所理解的ViewModel?
如果看过我上一篇文章的小伙伴应该都有所了解,ViewModel
是MVVM架构的一个层级,用来联系View和model之间的关系。而我们今天要说的就是官方出的一个框架——ViewModel。
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据
官方是这么介绍的,这里面有两个信息:
- 注重生命周期的方式。
由于ViewModel
的生命周期是作用于整个Activity的,所以就节省了一些关于状态维护的工作,最明显的就是对于屏幕旋转这种情况,以前对数据进行保存读取,而ViewModel
则不需要,他可以自动保留数据。
其次,由于ViewModel
在生命周期内会保持局部单例,所以可以更方便Activity的多个Fragment
之间通信,因为他们能获取到同一个ViewModel实例,也就是数据状态可以共享了。
- 存储和管理界面相关的数据。
ViewModel
层的根本职责,就是负责维护界面上UI的状态,其实就是维护对应的数据,因为数据会最终体现到UI界面上。所以ViewModel
层其实就是对界面相关的数据进行管理,存储等操作。
ViewModel 为什么被设计出来,解决了什么问题?
- 在
ViewModel
组件被设计出来之前,MVVM又是怎么实现ViewModel这一层级的呢?
其实就是自己编写类,然后通过接口,内部依赖实现View和数据的双向绑定。
所以Google出这个ViewModel组件,无非就是为了规范MVVM
架构的实现,并尽量让ViewModel这一层级只触及到业务代码,不去关心VIew层级的引用等。然后配合其他的组件,包括livedata,databindingrang
等让MVVM架构更加完善,规范,健硕。
- 解决了什么问题呢?
其实上面已经说过一些了,比如:
1)不会因为屏幕旋转而销毁,减少了维护状态的工作
2)由于在作用域内单一实例的特性,使得多个fragment
之间可以方便通信,并且维护同一个数据状态。
3)完善了MVVM
架构,使得解耦更加纯粹。
说说ViewModel原理。
- 首先说说是怎么保存生命周期
ViewModel2.0之前呢,其实原理是在Activity上add一个HolderFragment,然后设置setRetainInstance(true)
方法就能让这个Fragment在Activity重建时存活下来,也就保证了ViewModel的状态不会随Activity的状态所改变。
2.0之后,其实是用到了Activity的onRetainNonConfigurationInstance()
和getLastNonConfigurationInstance()
这两个方法,相当于在横竖屏切的时候会保存ViewModel的实例,然后恢复,所以也就保证了ViewModel的数据。
- 再说说怎么保证作用域内唯一实例
首先,ViewModel的实例是通过反射获取的,反射的时候带上application的上下文,这样就保证了不会持有Activity或者Fragment等View的引用。然后实例创建出来会保存到一个ViewModelStore
容器里面,其实也就是一个集合类,这个ViewModelStore 类其实就是保存在界面上的那个实例,而我们的ViewModel
就是里面的一个集合类的子元素。
所以我们每次获取的时候,首先看看这个集合里面有无我们的ViewModel
,如果没有就去实例化,如果有就直接拿到实例使用,这样就保证了唯一实例。最后在界面销毁的时候,会去执行ViewModelStore
的clear方法,去清除集合里面的ViewModel数据。一小段代码说明下:
public <T extends ViewModel> T get(Class<T> modelClass) {
// 先从ViewModelStore容器中去找是否存在ViewModel的实例
ViewModel viewModel = mViewModelStore.get(key);
// 若ViewModel已经存在,就直接返回
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
}
// 若不存在,再通过反射的方式实例化ViewModel,并存储进ViewModelStore
viewModel = modelClass.getConstructor(Application.class).newInstance(mApplication);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
}
LiveData 是什么?
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
官方介绍如下,其实说的比较清楚了,主要作用在两点:
数据存储器类
。也就是一个用来存储数据的类。可观察
。这个数据存储类是可以观察的,也就是比一般的数据存储类多了这么一个功能,对于数据的变动能进行响应。
主要思想就是用到了观察者模式
思想,让观察者和被观察者解耦,同时还能感知到数据的变化,所以一般被用到ViewModel中,ViewModel
负责触发数据的更新,更新会通知到LiveData
,然后LiveData再通知活跃状态的观察者。
var liveData = MutableLiveData<String>()
liveData.observe(this, object : Observer<String> {
override fun onChanged(t: String?) {
}
})
liveData.setVaile("xixi")
//子线程调用
liveData.postValue("test")
LiveData 为什么被设计出来,解决了什么问题?
LiveData
作为一种观察者模式设计思想,常常被和Rxjava
一起比较,观察者模式的最大好处就是事件发射的上游 和 接收事件的下游 互不干涉,大幅降低了互相持有的依赖关系所带来的强耦合性
。
其次,LiveData还能无缝衔接到MVVM
架构中,主要体现在其可以感知到Activity
等生命周期,这样就带来了很多好处:
不会发生内存泄漏
观察者会绑定到Lifecycle
对象,并在其关联的生命周期遭到销毁后进行自我清理。不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态
(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。自动判断生命周期并回调方法
如果观察者的生命周期处于STARTED
或RESUMED
状态,则 LiveData 会认为该观察者处于活跃状态,就会调用onActive
方法,否则,如果 LiveData 对象没有任何活跃观察者时,会调用onInactive()
方法。
说说LiveData原理。
说到原理,其实就是两个方法:
- 订阅方法,也就是
observe
方法。通过该方法把订阅者和被观察者关联起来,形成观察者模式。
简单看看源码:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
//...
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
public V putIfAbsent(@NonNull K key, @NonNull V v) {
Entry<K, V> entry = get(key);
if (entry != null) {
return entry.mValue;
}
put(key, v);
return null;
}
这里putIfAbsent
方法是讲生命周期相关的wrapper
和观察者observer
作为key和value存到了mObservers
中。
- 回调方法,也就是
onChanged
方法。通过改变存储值,来通知到观察者也就是调用onChanged
方法。从改变存储值方法setValue
看起:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
//...
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
这一套下来逻辑还是比较简单的,遍历刚才的map——mObservers
,然后找到观察者observer
,如果观察者不在活跃状态(活跃状态,也就是可见状态,处于 STARTED 或 RESUMED状态),则直接返回,不去通知。否则正常通知到观察者的onChanged方法。
当然,如果想任何时候都能监听到,都能获取回调,调用observeForever
方法即可。
参考
拜拜
有一起学习的小伙伴可以关注下️我的公众号——码上积木,每天三问面试真题,详细剖析,助你成为offer收割机。
ViewModel和LiveData问题思考与解答的更多相关文章
- Android面试题《思考与解答》11月刊
又来更新啦,Android面试题<思考与解答>11月刊奉上. 说说View/ViewGroup的绘制流程 View的绘制流程是从ViewRoot的performTraversals开始的, ...
- 【Medium 万赞好文】ViewModel 和 LIveData:模式 + 反模式
原文作者: Jose Alcérreca 原文地址: ViewModels and LiveData: Patterns + AntiPatterns 译者:秉心说 View 和 ViewModel ...
- Jetpack架构组件学习(2)——ViewModel和Livedata使用
要看本系列其他文章,可访问此链接Jetpack架构学习 | Stars-One的杂货小窝 原文地址:Jetpack架构组件学习(2)--ViewModel和Livedata使用 | Stars-One ...
- 转 Android Lifecycle、ViewModel和LiveData
转自:https://www.jianshu.com/p/982545e01d0a 1.概述 在I / O '17的时候,其中一个重要的主题是Architecture Components.这是一个官 ...
- Jetpack的ViewModel与LiveData
本文基于SDK 29 一.ViewModel与LiveData的作用: 1.viewModel: 数据共享,屏幕旋转不丢失数据,并且在Activity与Fragment之间共享数据. 2.LiveDa ...
- ViewModel、LiveData、DataBinding
ViewModel ViewModel的引入 如果系统销毁或重新创建界面控制器,则存储在其中的任何临时性界面相关数据都会丢失.例如,应用的某个 Activity 中可能包含用户列表.因配置更改而重新创 ...
- Jetpack 架构组件 LiveData ViewModel MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Android Jetpack组件 - ViewModel,LiveData使用以及原理
本文涉及的源码版本如下: com.android.support:appcompat-v7:27.1.1 android.arch.lifecycle:extensions:1.1.1 android ...
- Android 架构组件-Lifecycle、LiveData、ViewModel
Lifecycle Lifecycle组件包括LifecycleOwner.LifecleObserver,能方便监听Activity或者Fragment的生命周期. 步骤: 1.实现Lifecycl ...
随机推荐
- python3 连接数据库~
~目前记录的是针对python3写的数据库连接,不适用于pyhon2.python3如果想要与数据库进行连接,则需要先下载对应各数据库的插件包,然后导入包.python3的插件下载地址:https:/ ...
- centos7修改ssh端口及添加ssh监听端口
ssh 修改默认端口 [root@node-1 ~]# vi /etc/ssh/sshd_config 修改port 为 5522 重启[root@node-1 ~]# systemctl resta ...
- Java知识系统回顾整理01基础03变量07final关键字
一.final赋值 final 修饰一个变量,有很多种说法,比如不能改变等等 准确的描述是 当一个变量被final修饰的时候,该变量只有一次赋值的机会 二.在声明的时候赋值 i已经被赋值为5,所以这里 ...
- 05 C语言的数据类型
C语言的数据类型 在C 中,数据类型是用来声明不同类型的变量或函数的一个广泛的概念.变量的数据类型决定了变量存储占用的空间大小,以及如何去解释存储的位模式. C 中的数据类型可分为以下几大类: 序号 ...
- Apache Hudi与Apache Flink集成
感谢王祥虎@wangxianghu 投稿 Apache Hudi是由Uber开发并开源的数据湖框架,它于2019年1月进入Apache孵化器孵化,次年5月份顺利毕业晋升为Apache顶级项目.是当前最 ...
- 技术分享丨华为鲲鹏架构Redis知识二三事
摘要:华为云鲲鹏Redis,业界首个基于自研ARM-Based全栈整合的Redis云服务,支持双机热备的HA架构,提供单机.主备.Proxy集群.Cluster集群实例类型,满足高读写性能场景及弹性变 ...
- DE2资源集锦
1.The School of Electrical and Computer Engineering (ECE) at the Georgia Institute of Technology:htt ...
- Apache Jmeter 性能测试
今天在写性能测试报告的时候需要使用到数据,打算用做一下性能测试,然后在百度后发现了一款Apache开源的Jmeter压测工具 Jmeter概述: Apache JMeter是一款纯java编写负载功能 ...
- 从面试角度学完 Kafka
Kafka 是一个优秀的分布式消息中间件,许多系统中都会使用到 Kafka 来做消息通信.对分布式消息系统的了解和使用几乎成为一个后台开发人员必备的技能.今天码哥字节就从常见的 Kafka 面试题入手 ...
- 深入了解Redis(7)-缓存穿透,雪崩,击穿
redis作为一个内存数据库,在生产环境中使用会遇到许多问题,特别是像电商系统用来存储热点数据,容易出现缓存穿透,雪崩,击穿等问题.所以实际运用中需要做好前期处理工作. 一.缓存雪崩 1.概念 缓存雪 ...