当前位置: 首页 > 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,避免粘性事件传递给新观察者。

相关文章:

  • 13.ArkUI Navigation的介绍和使用
  • SIEMENS PLC程序解读 -Serialize(序列化)SCATTER_BLK(数据分散)
  • 关于位运算的一些小记
  • 速成GO访问sql,个人笔记
  • AI之FastAPI+ollama调用嵌入模型OllamaBgeEmbeddings
  • 用 ESP32 模拟 Wiegand 刷卡器:开发门禁系统必备的小工具
  • C++入门小馆: 深入了解STLlist
  • 【金仓数据库征文】-不懂数据库也能看懂!一文解析金仓技术介绍以典型应用
  • 电子病历高质量语料库构建方法与架构项目(数据遗忘篇)
  • Redis 集群切片全解析:四种常见技术的原理、优劣与应用
  • 【MQ篇】RabbitMQ的消费者确认机制实战!
  • HTML word属性
  • 文档驱动:“提纲挈领”视角下的项目管理中枢构建
  • SpringBoot 学习
  • 2025 Java 开发避坑指南:如何避免踩依赖管理的坑?
  • 【Linux网络编程】应用层协议HTTP(实现一个简单的http服务)
  • 粒子群优化算法(Particle Swarm Optimization, PSO)的详细解读
  • 项目质量管理
  • Cancer Cell发表医学AI综述,聚焦于人工智能与转化癌症研究的交叉领域
  • exec和spawn
  • 中青报:“猿辅导员工猝死”事件上热搜,是对健康职场环境的共同关切
  • 中国平安一季度净赚270亿降逾26%,营运利润增2.4%
  • 长三角数智文化产业基金意向签约会成功举办
  • 韩国对华中厚板征收临时反倾销税
  • 上海银行一季度净赚逾62亿增2.3%,不良贷款率与上年末持平
  • 上海未来亚洲研究会第六届会员大会举行,叶青当选会长