浅谈 LiveData 的通知机制

Lifecycle、LiveData、ViewModel讲解之LiveData Lifecycle、LiveData、ViewModel介绍 在 Android 框架中定义的大多数应用组件都存在生命周期。生命周期由操作系统或进程中运行的框架代码管理。它们是 Android 运作方式的核心,应用必须遵循它们。如果不这样做,可能会引发内存泄露甚至应用崩溃。 LiveData LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知. 阅读详情

LiveData 和 ViewModel 是 Google 官方的 MVVM 架构的一个组成部分。巧了,昨天分析了一个问题是 ViewModel 的生命周期导致的。今天又遇到了一个问题是 LiveData 通知导致的。而 ViewModel 的生命周期和 LiveData 的通知机制是它们的主要责任。所以,就这个机会我们也来分析一下 LiveData 通知的实现过程。

  1. 关于 ViewModel 的生命周期:《浅谈 LiveData 的通知机制》;
  2. 关于 MVVM 设计模式的基本应用,你可以参考这篇文章:《Android 架构设计:MVC、MVP、MVVM和组件化》.

1、一个 LiveData 的问题

今天所遇到的问题是这样的,

LiveData的问题

有两个页面 A 和 B,A 是一个 Fragment ,是一个列表的展示页;B 是其他的页面。首先,A 会更新页面,并且为了防止连续更新,再每次更新之前需要检查一个布尔值,只有为 false 的时候才允许从网络加载数据。每次加载数据之前会将该布尔值置为 true,拿到了结果之后置为 false. 这里拿到的结果是借助 LiveData 来通知给页面进行更新的。

现在,A 打开了 B,B 中对列表中的数据进行了更新,然后发了一条类似于广播的消息。此时,A 接收了消息并进行数据加载。过了一段时间,B 准备退出,再退出的时候又对列表中的项目进行了更新,所以此时又发出了一条消息。

B 关闭了,我们回到了 A 页面。但是,此时,我们发现 A 页面中的数据只包含了第一次的数据更新,第二次的数据更新没有体现在列表中。

用代码来描述的话大致是下面这样,

    // 类 A
    public class A extends Fragment {
    
        private boolean loading = false;

        private MyViewModel vm;

        // ......

        /**
         * Register load observer.
         */
        public void registerObservers() {
            vm.getData().observe(this, resources -> {
                loading = false;
                // ... show in list
            })
        }

        /**
         * Load data from server.
         */
        public void loadData() {
            if (loading) return;
            loading = true;
            vm.load();
        }

        /**
         * On receive message.
         */
        public void onReceive() {
            loadData();
        }
    }

    public class B extends Activity {

        public void doBusiness1() {
            sendMessage(MSG); // Send message when on foreground.
        }

        @Override
        public void onBackpressed() {
            // ....
            sendMessage(MSG); // Send message when back
        }
    }

    public class MyViewModel extends ViewModel {

        private MutableLiveData<Resoucres<Object>> data;

        public MutableLiveData<Resoucres<Object>> getData() {
            if (data == null) {
                data = new MutableLiveData<>();
            }
            return data;
        }

        public void load() {
            Object result = AsyncGetData.getData(); // Get data
            if (data != null) {
                data.setValue(Resouces.success(result));
            }
        }
    }

A 打开了 B 之后,A 处于后台,B 处于前台。此时,B 调用 doBusiness1() 发送了一条消息 MSG,A 中在 onReceive() 中收到消息,并调用 loadData() 加载数据。然后,B 处理完了业务,准备退出的时候发现其他数据发生了变化,所以又发了一条消息,然后 onReceive() 中收到消息,并调用 loadData(). 但此时发现 loading 为 true. 所以,我们后来对数据的修改没有体现到列表上面。

2、问题的原因

如果用上面的示例代码作为例子,那么出现问题的原因就是当 A 处于后台的时候。虽然调用了 loadData() 并且从网络中拿到了数据,但是调用 data.setValue() 方法的时候无法通知到 A 中。所以,loading = false 这一行无法被调用到。第二次发出通知的时候,一样调用到了 loadData(),但是因为此时 loading 为 true,所以并没有执行加载数据的操作。而当从 B 中完全回到 A 的时候,第一次加载的数据被 A 接收到。所以,列表中的数据是第一次加载时的数据,第二次加载事件丢失了。

解决这个问题的方法当然比较简单,可以当接收到事件的时候使用布尔变量监听,然后回到页面的时候发现数据发生变化再执行数据加载:

    // 类 A
    public class A extends Fragment {
    
        private boolean dataChanged;

        /**
         * On receive message.
         */
        public void onReceive() {
            dataChanged = true;
        }

        @Override
        public void onResume() {
            // ...
            if (dataChanged) {
                loadData();
            }
        }
    }

对于上面的问题,当我们调用了 setValue() 之后将调用到 LiveData 类的 setValue() 方法,

    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

这里表明该方法必须在主线程中被调用,最终事件的分发将会交给 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<T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    // 发送事件
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

然后,会调用 considerNotify() 方法来最终将事件传递出去,

    private void considerNotify(ObserverWrapper observer) {
        // 这里会因为当前的 Fragment 没有处于 active 状态而退出方法
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

这里会因为当前的 Fragment 没有处于 active 状态而退出 considerNotify() 方法,从而消息无法被传递出去。

3、LiveData 的通知机制

LiveData 的通知机制并不复杂,它的类主要包含在 livedata-core 包下面,总共也就 3 个类。LiveData 是一个抽象类,它有一个默认的实现就是 MutableLiveData.

LiveData 主要依靠内部的变量 mObservers 来缓存订阅的对象和订阅信息。其定义如下,使用了一个哈希表进行缓存和映射,

private SafeIterableMap<Observer<T>, ObserverWrapper> mObservers = new SafeIterableMap<>();

每当我们调用一次 observe() 方法的时候就会有一个映射关系被加入到哈希表中,

    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        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 对象。然后使用该对象对 owner 的生命周期进行监听。

这的 LifecycleBoundObserver 和 ObserverWrapper 两个类的定义如下,

    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

    private abstract class ObserverWrapper {
        final Observer<T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<T> observer) {
            mObserver = observer;
        }

        abstract boolean shouldBeActive();

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        void detachObserver() {}

        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }

上面的类中我们先来关注 LifecycleBoundObserver 中的 onStateChanged() 方法。该方法继承自 LifecycleObserver. 这里的 Lifecycle.Event 是一个枚举类型,定义了一些与生命周期相关的枚举值。所以,当 Activity 或者 Fragment 的生命周期发生变化的时候会回调这个方法。从上面我们也可以看出,该方法内部又调用了基类的 activeStateChanged() 方法,该方法主要用来更新当前的 Observer 是否处于 Active 的状态。我们上面无法通知也是因为在这个方法中 mActive 被置为 false 造成的。

继续看 activeStateChanged() 方法,我们可以看出在最后的几行中,它调用了 dispatchingValue(this) 方法。所以,当 Fragment 从处于后台切换到前台之后,会将当前缓存的值通知给观察者。

那么值是如何缓存的,以及缓存了多少值呢?回到之前的 setValue() 和 dispatchingValue() 方法中,我们发现值是以一个单独的变量进行缓存的,

    private volatile Object mData = NOT_SET;

因此,在我们的示例中,当页面从后台切换到前台的时候,只能将最后一次缓存的结果通知给观察者就真相大白了。

总结

从上面的分析中,我们对 LiveData 总结如下,

  1. 当调用 observe() 方法的时候,我们的观察者将会和 LifecycleOwner (Fragment 或者 Activity) 一起被包装到一个类中,并使用哈希表建立映射关系。同时,还会对 Fragment 或者 Activity 的生命周期方法进行监听,依次来达到监听观察者是否处于 active 状态的目的。
  2. 当 Fragment 或者 Activity 处于后台的时候,其内部的观察者将处于非 active 状态,此时使用 setValue() 设置的值会缓存到 LiveData 中。但是这种缓存只能缓存一个值,新的值会替换旧的值。因此,当页面从后台恢复到前台的时候只有最后设置的一个值会被传递给观察者。
  3. 2 中的当 Fragment 或者 Activity 从后台恢复的时候进行通知也是通过监听其生命周期方法实现的。
  4. 调用了 observe() 之后,Fragment 或者 Activity 被缓存了起来,不会造成内存泄漏吗?答案是不会的。因为 LiveData 可以对其生命周期进行监听,当其处于销毁状态的时候,该映射关系将被从缓存中移除。

以上。

(如有疑问,可以在评论中交流)


如果你喜欢这篇文章,请点赞!你也可以在以下平台关注我:

所有的文章维护在:Github, Android-notes

看完这篇文章,LiveData的用法竟然还可以这样用? 本文主要内容: 作用介绍 核心类介绍 基本使用 源码分析 – 横竖屏切换恢复 – 后台销毁恢复 ViewModel的主要工作: 本身主要是一个数据维护工具 将数据维护的工作从Activity上剥离,提供一个储存数据环境,自身机制可以解决开发中,数据受Activity生命周期影响产生数据丢失的问题 (主要为横竖屏切换以及在后台被销毁)。 通常结合LiveData使用。 作为一个纯数据维护工具... 阅读详情

相关推荐

Android开发中使用LiveData进行事件传递的常见问题及解决方法

它具有生命周期感知能力,可以确保数据只在活跃的生命周期所有者中传递,并在观察者处于非活跃状态时暂停传递,避免了内存泄漏和潜在的空指针异常。错误的生命周期所有者:LiveData通过感知生命周期来管理观察者,确保在观察者的生命周期所有者处于活跃状态时传递数据,并在非活跃状态时自动暂停传递。确保将观察者的生命周期所有者设置为正确的组件,例如在Activity中观察数据时,将观察者的生命周期所有者设置为Activity本身。数据未更新:LiveData的一个主要优点是可以自动更新观察者的数据。

DevScribe的博客 523

LiveData | observer引起的业务场景BUG

LiveData LiveData组件是Jetpack新推出基于观察者的消息订阅/分发组件,具有宿主(Activity/Fragment)生命周期感知能力,这种感知能力可确保LiveData仅分发消息给处于活跃状态的观察者,即只有处于活跃状态的观察者才能收到消息。 出现的问题 业务场景 A页面有一个持续性的业务任务,并同时播放BGM,任务的成功失败依赖后端下发的IM消息控制,在业务层则通过LiveData去分发结果 问题: A页面跳转到另一个页面B之后,任务仍然执行,BGM任然播放,但停留在页面B的时候,失

qq_37196748的博客 1719

深交所 vs 上交所:Level-2实时数据推送规则详解与策略适配指南

本文深度解析沪深Level-2实时数据推送规则的关键差异,包括逐笔成交、逐笔委托和10档行情接口的处理逻辑。针对高频交易策略,详细对比上交所与深交所在数据推送机制、时间戳处理及委托队列解析等方面的差异,并提供具体的策略适配方案和性能优化建议,帮助量化投资者提升跨市场交易稳定性。

weixin_27310417的博客 1162

Android Jetpack之LiveData

LiveData概述 LiveData是一个可观察的数据持有者类,和常规的observable不同,它是用来感知生命周期的,这意味着它遵守其他应用组件(activity、fragment、service等)的生命周期。这就确保它只会更新处于活动状态的组件。 如果一个类实现了android.arch.lifecycle.Observer,那么LiveData就认为这个类是STARTED或者RESUM...

mingyunxiaohai的专栏 570

从PSDK到ROS节点:实战GPS数据发布与CMake配置

本文详细介绍了如何从大疆PSDK获取GPS数据并封装为ROS节点的实战过程,包括PSDK编译、GPS数据订阅、ROS节点实现及CMake配置优化。通过具体代码示例和调试技巧,帮助开发者快速实现无人机传感器数据与ROS系统的集成,提升开发效率。

weixin_30843605的博客 457

linux中的sendmail发送邮件

linux下的sendmail发送邮件

无痕的博客 6416

Jetpack系列(三) — LiveData

fun updateTaps() { _userLiveData.postValue(User(“aa”, “bb”, 12)) } } switchMap()对存储在 LiveData 对象中的值应用函数 //HomeViewModel.kt class HomeViewModel : ViewModel() { private val _userIdLiveData = MutableLiveData() val user: LiveData = Transformations.switchMap(

m0_67595789的博客 293

02.1 知识图谱工具 Protege的下载安装与使用

Protege的下载安装与使用 本文是王昊奋老师《知识图谱》系列笔记第2.1篇。 文章目录Protege的下载安装与使用0 相关介绍0.0 Protege相关介绍0.1 Protégé的特点0.2 Protégé的用途1 Protege的下载1.1 准备工作:安装配置JAVA环境1.2 官网下载2 Protege的安装2.1 直接解压缩2.2 双击运行3 Protege的使用实例-创建类...

和而不流 7万+

利用Room+livedata的方式实现页面数据的实时联动更新

Room+LiveData

Oliver_YuCsdn的博客 680

Infineon MOSFET开关损耗计算实战指南

本文是英飞凌MOSFET开关损耗计算的实战指南。针对MOSFET发热问题,文章深入剖析了开关损耗的产生机理,并基于Infineon数据手册的关键参数,手把手推导了开通与关断损耗的工程计算公式。同时,指南强调了栅极电阻、寄生参数等实际因素对损耗的影响,并提供了从理论计算到实测优化的完整路径,帮助工程师精准评估与优化电源设计。

weixin_29212867的博客 440

详解ReID的各部分组成及Trick——基于FastReID

这一系列博客将基于京东开源的FastReID进行扩充,详细介绍了ReID的各个组成部分,一些有用的Trick,评价指标,常用数据集等,详细内容见下方的链接,总结不易,如有理解不正确之处,麻烦各位批评指正。 一、FastReID的中的baseline文件配置 二、Training strategy 三、Pre-processing 四、Backbone 五、Aggregation 六、Head 七、Loss 八、Distance Metric 九、Post-processing 十、Evaluation 十一

藏晖的博客 9162

浅谈LiveData的通知过程

Java 注解及其两种使用方法一般的,注解在 Android 中有两种应用方式,一种方式是基于反射的,即在程序的运行期间获取类信息浅谈 LiveData 的通知机制LiveData 和 ViewModel 一起是 Google 官方的 MVVM 架构的一个组成

夜游神 1096

国产信创麒麟操作系统 (kylin v10 x86架构) 离线安装nginx步骤(亲测,超详细步骤)、修改nginx配置文件、启动、重启、关闭nginx记录(亲测有效)

国产信创麒麟操作系统 (kylin v10 x86架构) 离线安装nginx步骤(亲测,超详细步骤)、修改nginx配置文件、启动、重启、关闭nginx记录(亲测有效)

qq445829096的博客 2755

Android Architecture Components (AAC)(B)

摘要:浅谈 Android Architecture Components 使用和原理 1、Lifycycle(AAC) 使用详解 2、LiveData 和 ViewModel (AAC)

T的专栏 376

8、硬件工程师笔试面试知识器件篇——二极管(2025版本)

当二极管正向偏置时(阳极相对于阴极为正电压),PN结内部的电场减小,允许电子和空穴复合,形成电流。而当二极管反向偏置时,PN结的电场增强,阻止电流通过。在接触区域,P型半导体中的空穴和N型半导体中的自由电子会相互扩散和复合,形成一个没有自由载流子的耗尽区。在设计电子电路时,平衡二极管的正向电压降(Vf)和最大电流(I)是一个重要的考虑因素,因为这两者直接影响电路的性能和效率。通过这些步骤,可以确保在设计电子电路时选择的二极管能够安全地处理预期的最大正向电流和最大反向电压,从而提高电路的可靠性和安全性。

XU157303764的博客 3500

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

LiveData常见问题

LiveData。

riqthen的博客 1433

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Jetpack之LiveData讲解

【1】关于LiveData ① LiveData,原意是 活着的数据。 数据还能有生命? 先来看下官方的定义: LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity/Fragment)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。 拆解开来: LiveData是一个数据持有者,给源数据包装一层。 源数据使用LiveData包装后,可以被observer观察

zjcoming 436

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Android LiveData我的理解

LiveData用大众语言来来讲,是一个观察者,也是一个数据持有类或者可以称为一个数据的包裹类。它有别于其他的观察者的重点是,他具有生命周期感知能力,这里生命周期指的是activities, fragments, or services 的生命周期。 讲到LiveData,就想到DataBinding,想到MVVM。LiveData的确一开始是为这种模式而设计出来的。LiveData搞懂,可以说...

alcoholdi的专栏 6702

MVVM+DataBing+LiveData+RxJava架构的初步认识(一)

本博客仅为小白个人学习记录,不作为任何教学指导,读者所见内容仅供参考,如有错误,欢迎指正,非常感谢。 MVVM+DataBing+LiveData+RxJava架构 先看Demo的架构图示: 首先在model中,也就是数据来源,通过RxJava+Retrofit的方式(单例类Rretrofit对象,Json解析,网络接口等),获得从APi的网络数据。 获得数据后,需要调用viewmodel类中相应的属性,这个属性类型是MutableLiveData,将数据赋值给livedata。 由于在Activity

qq_41904106的博客 976

Jetpack 之 LiveData

Jetpack 是 Google 官方力推的一个套件,它由许多库组成,这些库的出现意在帮助开发者简化业务逻辑之外的部分,包括开发架构优化、避免内存泄漏、性能优化等方面。之前我也零零散散用到过一些,现在准备做个记录,帮助自己梳理以及再次熟悉一下各组件。 一、LiveData 是什么 ...

禽兽先生不禽兽 1619

android之livedata使用

一、前言 LiveData 是一个可观察的数据存储类, 并且具有Activity or Framgent生命周期的感知能力,livedata会将存储的数据,发送给正处于活跃的观察者observer。 使用LiveData的优势 UI和数据保持一致 避免内存泄漏 不会因 Activity 停止而导致崩溃 自动感知生命周期 解决Configuration changes问题 共享资源:使用单例扩展LiveData 二、相关API 1、postValue() 1.此方法可以在主线程或者子线程中调用,最终会在主

lss0555的博客 2081

LiveData基本使用

LiveData 基本使用

qq_52019658的博客 520

LiveData基本教程

转载请注明链接: https://blog.youkuaiyun.com/feather_wch/article/details/88400574 LiveData基本教程 版本号:2019-03-18(19:30) 文章目录LiveData基本教程简介优势使用LiveData 简介 1、LiveData的简介 LiveData是一种类,持有可被观察的数据。 LiveData是一种可感知生命周期的组件,...

feather(猎羽) 6152

Jetpack LiveData使用(一)

目录 LiveData有哪些优势? LiveData 使用 LiveData有哪些优势? LiveData 是一个可以被观察的数据持有类,它可以感知 Activity、Fragment或Service 等组件的生命周期。LiveData 是可被观察的数据持有类。具有生命周期(Activity/Fragment/Service)感知的(确保active状态下接受data更新) 简单来说,他主要有一下优点。 它可以做到在组件处于激活状态的时候才会回调相应的方法,从而刷新相应的 UI。 不用担心.

王温暖的博客 593

LiveData的使用和原理

private val mutableLiveData = MutableLiveData<String>(); override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mutableLiveData.observe(this, Obser.

zhaonhui的博客 2178
上一篇: 浅谈 ViewModel 的生命周期控制
下一篇: Android 系统源码-1:Android 系统启动流程源码分析
开发者如是说
博客等级 码龄10年 67粉丝 163原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值