浅谈 LiveData 的通知机制

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

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

一、一个 LiveData 的问题

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

LiveData的问题

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

如今,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. 因此,咱们后来对数据的修改没有体现到列表上面。架构

二、问题的缘由

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

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

// 类 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() 方法,从而消息没法被传递出去。

三、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 的生命周期进行监听。

这的 LifecycleBoundObserverObserverWrapper 两个类的定义以下,

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

相关文章
相关标签/搜索