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

kotlin与MVVM结合使用总结(三)

1. MVVM 架构详细介绍及源码层面理解

整体架构

MVVM(Model - View - ViewModel)架构是为了解决视图和数据模型之间的耦合问题而设计的。它通过引入 ViewModel 作为中间层,实现了视图和数据的分离,提高了代码的可维护性和可测试性。

  • View(视图层):在 Android 中,View 主要关联 Activity、Fragment 以及 XML 布局文件等,负责呈现界面与响应用户交互。像 Activity 里设置布局、初始化视图元素,以及处理用户点击等操作都属于 View 范畴。例如在一个登录界面 Activity 里,布局文件定义了输入框、按钮等 UI 元素的样式与位置,Activity 则处理按钮点击事件,这些都在 View 层完成。
  • Model(数据模型层):负责数据的获取、存储与处理。比如从网络请求用户信息、将数据存储到本地数据库等。在电商应用中,从服务器获取商品列表数据,或是将用户的购物车信息保存到本地数据库,都由 Model 层执行。
  • ViewModel(视图模型层):作为连接 View 与 Model 的纽带,承担关键的界面显示逻辑处理任务。它从 Model 获取数据,并将其转换为适合 View 展示的形式。例如在新闻应用中,Model 获取到原始新闻数据列表,ViewModel 可对数据进行加工,如截取新闻摘要、处理图片链接等,让数据能更好地在 View 中展示。在数据更新时,ViewModel 通过 LiveData 等机制通知 View 进行相应更新。从源码层面看,ViewModel 借助 ViewModelProvider 来创建与管理。ViewModelProvider 内部运用 ViewModelStore 存储 ViewModel 实例,确保配置变更(如屏幕旋转)时,ViewModel 实例不会被销毁,维持数据的稳定性。
观察者模式在 MVVM 中的应用

在 MVVM 架构里,观察者模式发挥着核心作用。ViewModel 持有数据,以 LiveData 为例,View 作为观察者监听 LiveData 数据变化。LiveData 内部维护了观察者列表,当数据变更时,会调用 dispatchingValue 方法遍历观察者列表。在 considerNotify 方法中,判断观察者状态,若活跃则通过 observer.mObserver.onChanged((T) mData) 通知观察者,View 接收到通知后更新界面,实现了 View 与 ViewModel 低耦合通信,这在诸多面试真题里都有涉及,是理解 MVVM 架构的关键。ViewModel 是通过 ViewModelProvider 来创建和管理的。

2. LiveData 实例化方法及源码分析

实例化方法
  • MutableLiveDataMutableLiveData 是 LiveData 的子类,它公开了 setValue() 和 postValue() 方法,允许外部修改其持有的数据。
MutableLiveData<String> liveData = new MutableLiveData<>();

在源码中,MutableLiveData 只是简单地继承了 LiveData 并暴露了修改数据的方法。

public class MutableLiveData<T> extends LiveData<T> {@Overridepublic void postValue(T value) {super.postValue(value);}@Overridepublic void setValue(T value) {super.setValue(value);}
}
  • 使用 Transformations 类进行转换Transformations 类提供了一些静态方法,如 map() 和 switchMap(),可以对 LiveData 进行转换。
LiveData<Integer> source = new MutableLiveData<>();
LiveData<String> transformed = Transformations.map(source, input -> "Transformed: " + input);

map() 方法的源码实现如下:

public static <X, Y> LiveData<Y> map(LiveData<X> source,final Function<X, Y> mapFunction) {final MediatorLiveData<Y> result = new MediatorLiveData<>();result.addSource(source, new Observer<X>() {@Overridepublic void onChanged(@Nullable X x) {result.setValue(mapFunction.apply(x));}});return result;
}

3. LiveData 如何实现生命周期绑定问题

LiveData 生命周期绑定机制在面试中频繁被问到,其实现依赖于 Android 的 Lifecycle 组件。

         当调用 LiveData.observe() 方法时,会创建 LifecycleBoundObserver 对象,它实现了 LifecycleEventObserver 接口来监听 LifecycleOwner(如 Activity、Fragment)的生命周期变化。在 observe() 方法源码中,先检查 LifecycleOwner 状态,若已销毁则直接返回;否则创建 LifecycleBoundObserver 并添加到观察者列表,同时将其注册到 LifecycleOwner 的生命周期观察者中。

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");if (owner.getLifecycle().getCurrentState() == DESTROYED) {return;}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);
}

LifecycleBoundObserver 的 onStateChanged 方法会在 LifecycleOwner 生命周期状态变化时被调用。在此方法中,根据生命周期状态决定是否更新观察者。当状态变为 DESTROYED 时,从 LiveData 的观察者列表移除该观察者,防止内存泄漏;当状态为 STARTED 或 RESUMED 时,认为观察者活跃,可接收数据更新。

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {@NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {super(observer);mOwner = owner;}@Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {removeObserver(mObserver);return;}activeStateChanged(shouldBeActive());}@Overrideboolean isAttachedTo(LifecycleOwner owner) {return mOwner == owner;}@Overridevoid detachObserver() {mOwner.getLifecycle().removeObserver(this);}
}

这种机制确保 LiveData 仅在 LifecycleOwner 处于活跃状态(STARTED 或 RESUMED)时更新观察者,有效避免内存泄漏与空指针异常,是 LiveData 的重要特性。

4. LiveData 粘性事件的深入分析

粘性事件的概念

粘性事件是指当一个观察者注册到 LiveData 时,即使该 LiveData 在观察者注册之前已经有了更新,观察者仍然会接收到这些之前的更新。这是因为 LiveData 会记录最新的值,当有新的观察者注册时,会立即将最新的值发送给它。

源码层面分析

在 LiveData 的 observe() 方法中,当新的观察者注册时,会调用 dispatchingValue() 方法,该方法会检查观察者的状态,并将最新的值发送给它。

private void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<? super 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;}if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;// 调用观察者的 onChanged 方法,发送最新的值observer.mObserver.onChanged((T) mData);
}

在 considerNotify() 方法中,会比较观察者的 mLastVersion 和 LiveData 的 mVersion,如果 mLastVersion 小于 mVersion,则会调用观察者的 onChanged() 方法,将最新的值发送给它。

解决粘性事件的方法

为了避免粘性事件的影响,可以考虑使用一些第三方库,如 SingleLiveEvent 或自定义 LiveData 实现。以下是一个简单的自定义 LiveData 实现,用于避免粘性事件:

import androidx.lifecycle.LiveData;import java.util.concurrent.atomic.AtomicBoolean;public class NonStickyLiveData<T> extends LiveData<T> {private final AtomicBoolean mPending = new AtomicBoolean(false);@Overridepublic void observeForever(@NonNull Observer<? super T> observer) {super.observeForever(new Observer<T>() {@Overridepublic void onChanged(T t) {if (mPending.compareAndSet(true, false)) {observer.onChanged(t);}}});}@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {super.observe(owner, new Observer<T>() {@Overridepublic void onChanged(T t) {if (mPending.compareAndSet(true, false)) {observer.onChanged(t);}}});}@Overrideprotected void setValue(T value) {mPending.set(true);super.setValue(value);}@Overrideprotected void postValue(T value) {mPending.set(true);super.postValue(value);}
}

在这个自定义的 LiveData 中,使用 AtomicBoolean 来标记是否有新的值需要发送,只有当 mPending 为 true 时,才会调用观察者的 onChanged() 方法,从而避免了粘性事件的影响。

粘性事件总结

        在 LiveData 机制里,不活跃观察者(对应 LifecycleOwner 处于 STOPPED 或 PAUSED 状态)正常情况下不会接收数据更新事件。只有当观察者再次变为活跃状态时,LiveData 才会将最新数据发送给它。这是因为在 LifecycleBoundObserver 的 shouldBeActive 方法中,依据 LifecycleOwner 的当前生命周期状态判断观察者是否活跃,不活跃则不进行数据分发。

       然而,LiveData 存在粘性事件问题,这在面试中常被提及。粘性事件指新观察者注册时,即便 LiveData 之前已有更新,观察者仍会收到这些之前的更新数据。从源码层面分析,在 LiveData 的 observe() 方法中,新观察者注册后会调用 dispatchingValue() 方法。在 dispatchingValue() 内部的 considerNotify() 方法里,通过比较观察者的 mLastVersion 和 LiveData 的 mVersion 来决定是否通知观察者。若 mLastVersion 小于 mVersion,则调用观察者的 onChanged() 方法发送最新数据,导致粘性事件发生。

为解决粘性事件问题,常见方法如下:

  • 使用 SingleLiveEvent:自定义一个继承自 MutableLiveData 的类,重写相关方法确保事件只被消费一次。例如在一些开源项目中,SingleLiveEvent 类通过设置标志位,在 observe() 方法中判断标志位,仅在首次观察时触发数据更新,后续不再响应之前的粘性数据。
  • 使用 Event 包装类:将数据包装在 Event 类中,通过标记数据是否已被处理来避免重复触发。在观察者获取数据时,先检查标记位,若未处理则处理数据并设置标记位,防止重复处理粘性数据。
  • 使用 MediatorLiveDataMediatorLiveData 可监听其他 LiveData 变化,并在必要时过滤粘性事件。通过添加源 LiveData 的观察者,在数据变化时进行相应处理,如更新自身数据后移除源 LiveData,避免粘性事件传递给新观察者。
http://www.xdnf.cn/news/139879.html

相关文章:

  • (Go Gin)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
  • 防火墙技术深度解析:从包过滤到云原生防火墙的部署与实战
  • 【1】GD32 系统架构、内核、中断系统、存储器系统
  • IDEA编写flinkSQL(快速体验版本,--无需配置环境)
  • Vue3后代组件多祖先通讯设计方案
  • OpenCV 图形API(63)图像结构分析和形状描述符------计算图像中非零像素的边界框函数boundingRect()
  • 52.[前端开发-JS实战框架应用]Day03-AJAX-插件开发-备课项目实战-Lodash
  • ubuntu20.04安装x11vnc远程桌面
  • AI数据分析的利器:解锁BI工具的无限潜力
  • android将打包文件的password和alias写入到本地文件
  • 区块链如何达成共识:PoW/PoS/DPoS的原理、争议与适用场景全解
  • 一些有关ffmpeg 使用(1)
  • LSPatch官方版:无Root Xposed框架,自由定制手机体验
  • MySQL的日志--Undo Log【学习笔记】
  • MCP认证考试技术难题实战破解:从IP冲突到PowerShell命令的深度指南
  • Hadoop进阶之路
  • 第100+39步 ChatGPT学习:R语言实现Xgboost SHAP
  • AI网络渗透kali应用(gptshell)
  • 第十二天 使用Unity Test Framework进行自动化测试 性能优化:Profiler分析、内存管理
  • 【QQmusic自定义控件实现音乐播放器核心交互逻辑】第三章
  • PyCharm 中 FREECAD 二次开发:从基础建模到深度定制
  • uni-app中获取用户实时位置完整指南:解决权限报错问题
  • 运维之SSD硬盘(SSD hard Drive for Operation and Maintenance)
  • Spring Cloud Gateway 如何将请求分发到各个服务
  • AI 工程师崛起:科技浪潮下的新兴力量
  • 机器学习的基本概念
  • 纯HTMLCSS静态网站——元神
  • 论文阅读笔记——ZeroGrasp: Zero-Shot Shape Reconstruction Enabled Robotic Grasping
  • 什么是视频上墙
  • 【研究学习】开源调制识别数据集