LiveData 源码分析

作者: 丘卡皮 | 来源:发表于2022-05-20 15:11 被阅读0次

    前言

    LiveData其实已经不是一个新鲜的东西了,我是在使用MVVM框架的时候开始接触到LiveData的,LiveData用起来确实很方便。

    LiveData是一个具有感知组件生命周期的类,如Activity、Fragment、Service,只要和LiveData进行了绑定,LiveData就能感知到它们的生命周期。

    LiveData的基本使用我们就不讲了,因为它的使用非常简单,不懂的话网上一搜一大把。

    那么,LiveData有什么好处呢?

    1、监听与其绑定的界面的生命周期,因此使用LiveData我们就不需要手动管理它的生命周期了

    2、组件能及时响应LiveData的数据变化,组件总能拿到LiveData的最新数据。当然,被绑定的组件响应LiveData是有一定的前提的,那就是LiveData数据发生变化 且 组件处于活跃状态

    也就是说,LiveData数据即使发生了变化,也不一定会响应onChanged函数,因为它必须要求LiveData数据所在的界面处于活跃状态,才会响应onChanged函数。

    3、 生命周期自动解绑,因为其能够在组件销毁的时候自动解绑,大大降低了应用产生内存泄露的概率

    LiveData确实能解决内存泄漏问题,但是如果我们使用不当,其实还是会出现内存泄漏的,

    例如,有一个Activity,Activity包含了一个Fragment,Fragment中有一个LiveData,因此Fragment引用了LiveData。

    然后LiveData通过observe方法把Activity当作owner进行了绑定,那么这时候,LiveData的生命周期将和Activity一样。如果这时候因为某种原因,Fragment被销毁了,那么LiveData将不会被销毁,因为它被Activity引用着。LiveData本该回收却无法被回收,那么LiveData就发生内存泄漏了。

    源码分析

    下面我们进入源码看看LiveData的原理。

    observe方法

    LiveData是如何监听与其绑定的界面的生命周期的?

    这就得从LiveData的与组件的绑定方法开始看了,所以我们来看看LiveData的observe方法。

    // LiveData.java;
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        //如果owner的生命周期为DESTROYED,直接return
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        //将外部传进的observer对象封装成一个LifecycleBoundObserver对象
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //将observer和wrapper存放到map中
        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);
    }
    

    首先,将外部传进的observer对象封装成LifecycleBoundObserver对象,对象名称为wrapper,其实LifecycleBoundObserver就是一个可以监听生命周期的类,我们可以看看LifecycleBoundObserver类的构造方法:

    LiveData.LifecycleBoundObserver类:
    
    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
            @NonNull
            final LifecycleOwner mOwner;
    
            LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
                super(observer);
                mOwner = owner;
            }
            
            // …………………………
    }
    
    @Deprecated
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
    public interface GenericLifecycleObserver extends LifecycleEventObserver { }
    
    public interface LifecycleEventObserver extends LifecycleObserver {
        void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
    }
    

    首先,我们也可以看到,LifecycleBoundObserver实现了GenericLifecycleObserver这个接口,而实际上,这个接口最终继承自LifecycleObserver这个接口的,

    LifecycleObserver是不是有点眼熟?

    没错,它是我们在使用lifecycle的时候应该addObserver时需要传递的参数。

    所以,我们通过new LifecycleBoundObserver(owner, observer)方法得到的wrapper实际上就是一个LifecycleObserver。

    在LifecycleBoundObserver类构造方法中,将observer传给其父类使用,而其父类是ObserverWrapper,

    我们看看ObserverWrapper的构造方法:

    // LiveData.ObserverWrapper类:
    private abstract class ObserverWrapper {
        final Observer<? super T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;
    
        ObserverWrapper(Observer<? super T> observer) {
            mObserver = observer;
        }
        
        // ………………………………
    }
    

    可以看到,实际上只是把observer赋值给ObserverWrapper类的mObserver成员变量罢了。

    我们继续回到LiveData的observe方法:

    // LiveData.java;
    private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
                new SafeIterableMap<>();
                
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        //如果owner的生命周期为DESTROYED,直接return
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        //将外部传进的observer对象封装成一个LifecycleBoundObserver对象
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //将observer和wrapper存放到mObservers中
        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);
    }
    

    mObservers.putIfAbsent(observer, wrapper)这行代码,将observer作为键,wrapper作为值存放到mObservers中,并且会返回一个ObserverWrapper对象。

    mObservers是一个由链表实现的支持键值对存储的数据结构,同时,它支持在遍历的过程中删除任意元素,我们可以暂且把它理解为保存observer对象的集合。

    如果ObserverWrapper不为null,则observe这个方法会直接return。ObserverWrapper在什么情况不为null呢?

    如果观察者observer已经在mObservers这个列表中,并且observer已经有另一个所有者owner,就会不为null。

    这是为了防止同一个observer被多个LifecycleOwner绑定,即一个LiveData中的同一个observer只能跟一个LifecycleOwner绑定。否则会抛出“Cannot add the same observer with different lifecycles”的异常。

    在observe方法的最后,调用了owner.getLifecycle().addObserver(wrapper)方法,使得wrapper和owner的生命周期进行了绑定。

    也就是说,observer此时就可以监听到owner对应的界面的生命周期的变化了!!

    setValue方法

    那么,setValue方法应该是我们用得最多的LiveData方法了,很明显它是用来更新LiveData的数据的,那么它是怎么实现的呢?我们来看下:

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

    首先看到,setValue方法有个@MainThread注解,说明这个方法只能在主线程中使用,然后在setValue中,将value赋值给mData变量,同时,mVersion变量加1,

    mVersion就是数据的版本,说白了就是用来标记数据是否变化的,后面我们还会见到,这里先略过。

    方法最后,调用了dispatchingValue(null)方法,我们进入该方法:

    // LiveData.java:
    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;
    }
    

    可以看到,dispatchingValue方法中,有个while循环,在while循环里面,由于initiator为null,所有走else分支的代码,这里又有个for循环,这个for循环是用来遍历

    mObservers中的observer对象的,

    然后通过considerNotify方法将遍历到的observer对象进行处理

    我们看看considerNotify方法:

    // LiveData.java:
    private void considerNotify(ObserverWrapper observer) {
        //observer对象不处于活跃状态,直接return
        if (!observer.mActive) {
            return;
        }
      
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }
    

    可以看到,首先做了一个判断,外部传入的observer对象是否处于活跃状态,不处于活跃状态,则直接return这个方法。从这里就可看出,

    即使数据更新了,但如果界面不处于活跃状态,也不会对数据更新做响应

    然后往下看,我又看到了mVersion这个变量,以及一个mLastVersion变量,

    判断observer.mLastVersion这个变量如果小于mVersion,会把mVersion的值赋值给observer.mLastVersion变量,

    这种情况就说明数据是新的数据,就可以执行observer的onChanged方法把mData回调出去了。

    postValue方法
    我们先来看postValue方法。

    // LiveData.java:
    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
    

    postValue方法中,外部传进来的value会赋值给mPendingData,方法的最后会调用postToMainThread方法,postToMainThread,看这个方法的命名应该也能看出来一些东西,就是将这个runnable发布到主线程处理,我们进入postToMainThread详细看看:

    // ArchTaskExecutor.java:
    @Override
    public void postToMainThread(Runnable runnable) {
        mDelegate.postToMainThread(runnable);
    }
    

    它通过调用mDelegate的postToMainThread方法,mDelegate实际上就是DefaultTaskExecutor这个类,那么我们再进入这个类的postToMainThread看看:

    DefaultTaskExecutor.java:
    @Override
    public void postToMainThread(Runnable runnable) {
        if (mMainHandler == null) {
            synchronized (mLock) {
                if (mMainHandler == null) {
                    mMainHandler = new Handler(Looper.getMainLooper());
                }
            }
        }
        //noinspection ConstantConditions
        mMainHandler.post(runnable);
    }
    

    在这个方法中,先判断mMainHandler对象是否为null,如果为null,就创建一个,注意,它是通过Looper.getMainLooper()参数来创建的,说明这个这个mMainHandler就是主线程的handler对象。

    最后调用mMainHandler.post(runnable)方法,将runnable对象交给主线程的handler来处理。

    那么runnable执行了什么东西呢?

    我们回到postValue方法,发现runnable就是mPostValueRunnable,我们看mPostValueRunnable是怎么定义的:

    // LiveData.java:
    private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            setValue((T) newValue);
        }
    };
    

    我们看到它的run方法,先定义了个newValue变量,然后加锁对newValue赋值为mPendingData变量,mPendingData是在postValue方法内第三行赋值的,前面已经看到过了,然后将mPendingData置为NOT_SET。最后调用setValue方法。

    所以,postValue方法实际上就是将线程切到主线程去处理setValue方法。

    LiveData对生命周期的响应

    举个例子:

    有一个界面对一个LiveData对象进行了监听,当界面处于非活跃状态时,如果我们更新了LiveData的数据,那么界面并不会马上响应LiveData的变化(因为界面处于非活跃状态嘛)。当界面重新回到活跃状态时,刚刚更新的LiveData数据会马上进行响应。

    为什么界面重新回到活跃状态时(比如activity从onPause状态回到onResume状态),刚刚更新的LiveData数据会马上进行响应呢?

    前面已经介绍过,在LiveData的observe方法中,通过owner.getLifecycle().addObserver(wrapper)方法,会使得wrapper和owner的生命周期进行了绑定,

    也就是说,owner生命周期变化,wrapper就能在onStateChanged这个方法中监听到。

    假设owner是个Activity,那么当Activity处于非活跃状态时,对LiveData进行了setValue操作,则在considerNotify方法的开头会直接被return,也就走不到observer.mObserver.onChanged方法了,也就不能接收到LiveData的数据变化。

    当Activity回到活跃状态,wrapper(即LifecycleBoundObserver)的onStateChanged方法就会被回调:

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

    如果Activity处于活跃状态时,就会执行到onStateChanged中的activeStateChanged方法:

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        // immediately set active state, so we'd never dispatch anything to inactive
        // owner
        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();
        }
        //如果处理活跃状态,就执行dispatchingValue(this)方法
        if (mActive) {
            dispatchingValue(this);
        }
    }
    

    首先可以看到,mActive在这里被赋值。然后看到最后三行,判断如果处于活跃状态,就执行dispatchingValue(this)方法,那么这时又回到了setValue的那个流程中,也就是说,Activity生命周期变成活跃状态后,又能触发LiveData的值的更新了。

    LiveData的自动解绑

    我们知道,LiveData能大大降低内存泄漏出现的可能,原因就是因为它能够自动跟它绑定的组件进行解绑,那么这是怎么做到的呢?还是看到LifecycleBoundObserver这个内部类:

    LiveData.java:
    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
            @NonNull
            final LifecycleOwner mOwner;
    
            LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
                super(observer);
                mOwner = owner;
            }
    
            @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);
            }
    }
    

    onStateChanged方法中,如果监听到owner生命处于DESTROYED时,会执行LiveData类中的removeObserver方法,它的实现如下:

    @MainThread
    public void removeObserver(@NonNull final Observer<? super T> observer) {
        assertMainThread("removeObserver");
        ObserverWrapper removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }
        //执行LifecycleBoundObserver的detachObserver方法
        removed.detachObserver();
        //执行ObserverWrapper的activeStateChanged方法
        removed.activeStateChanged(false);
    }
    

    removeObserver方法的主要流程分三个步骤:

    1、首先从mObservers集合中移除当前的observer对象

    2、执行removed.detachObserver(),这行代码实际上会执行LifecycleBoundObserver的detachObserver方法,这里是真正将mOwner与mObserver解绑的地方

    3、执行removed.activeStateChanged(false),这行代码实际上会执行ObserverWrapper的activeStateChanged方法,并把false传入,这个方法前面已经看过了,就不详细讲了

    LiveData的mData

    这里简单的介前面已经介绍过,在setValue方法中,value会赋值给LiveData的mData这个成员变量,我们我看看这个mData。

    private volatile Object mData = NOT_SET;
    

    可以看到,它是被volatile修饰的,对java并发有一定了解应该都知道,volatile是用来保证变量的可见性的,防止并发的情况下,多个线程对同一个变量进行修改,出现数据数据不一致的问题。

    假设有一个场景,我想用两个线程对一个公共变量x进行加1操作(x的初始值是0),我希望得到的结果是x等于2。

    其实,大多数情况下,是能正常得到2这个值的,但是!。

    有种情况,线程1和线程2同时从主内存中拿到了x,并把x拷贝到自己的工作内存进行操作,两个线程都对x这个值进行了加1操作,则在这两个线程的工作内存中,x的值都变成了1,

    对值进行操作完毕后,然后线程1和线程2就把x的值写回到主内存中,那么此时主内存中的x的值必然是1。

    所以在这种情况,数据就不是我想要的了,那么怎样保证这个值一定是2呢?那就是使用volatile了。

    由此我们也看出。LiveData中的mData是线程安全的。

    LiveData跨组件通信

    我们已经知道LiveData可以及时通知组件数据发生变化,还能自动解除绑定,但是LiveData的用途还不止这些。

    不知道大家有没有用过EventBus,这个东西可以代替Android传统的Intent、Handler、Broadcast或接口回调,在Fragment、Activity、Service线程之间传递数据,执行方法。EventBus最大的特点就是简洁、解耦,它的框架思想就是消息的发布和订阅,它通过解耦发布者和订阅者简化Android事件传递。

    其实,通过LiveData,我们也能实现类似于EventBus的框架,并且使用后不需要手动解除绑定,更加方便和优雅。那么,怎么实现呢?

    跨组件通信初探

    首先能想到,可以将一个LiveData设置为static的,这样不同的Activity或Fragment就都能访问这个变量,那么监听这个变量值变化也不成问题。这个方法确实能实现LiveData跨Activity/Fragment通信。

    举个例子:假设有两个界面,分别是Activity1和Activity2,Activity1通过LiveData发送数据,然后在Activity2中接受数据。

    那么我们可以新建一个MyStaticLiveData类,用来保存公共的LiveData:

    class MyStaticLiveData {
        companion object {
            val liveData = MutableLiveData<String>()
        }
    }
    

    然后Activity1中发送数据,并且有个button用来跳转到Activity2:

    class Activity1 : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_live_data)
    
            MyStaticLiveData.liveData.value = "data from LiveDataActivity."
        }
    
        fun toLiveData2Activity(view: View) {
            startActivity(Intent(this, Activity2::class.java))
        }
    }
    

    Activity2中,将公共的LiveData和Activity2进行绑定监听,这样就能接受到Activity1发送过来的数据了,并且我们也不需要在onDestroy中解绑LiveData,因为它会自动解绑。

    class Activity2 : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_live_data2)
    
            MyStaticLiveData.liveData.observe(this, {
                tv2.text = it
            })
        }
    }
    

    这样看来,确实实现了EventBus粘性事件的功能,并且用法更加简洁。

    但是它的缺点也是很明显的,它不够优雅,而且,每当我想搞个新的LiveData的时候,都需要在MyStaticLiveData中手动创建,另外,没有一个东西统一来管理这些LiveData,并且,每个LiveData都是static的,所以,开发中不建议这么写,这里只做演示用,不建议用到实际开发场景中 !!!。

    另外,在开发过程中,粘性事件大部分情况下我们都是不需要用到的。而更多的场景是,我们希望监听者能在注册监听后才开始接收发送过来的消息,在注册前发送的消息一律不接收。

    数据倒灌

    我们刚刚演示的例子,业内有另一种更常见的叫法,数据倒灌。

    一句话概括就是,先给LiveData设置了value,然后监听者才开始对LiveData进行监听,这时LiveData的value会立马回调给监听者。

    产生数据倒灌的原因
    这还得从源码的角度进行分析,我们先看setValue方法:

    // LiveData.java:
    ......................................
    static final int START_VERSION = -1;
    private int mVersion = START_VERSION;
    ......................................
    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    

    注意这里的mVersion,在setValue中进行了自增操作。另外,mVersion的值默认为-1。所以当我们第一次调用setValue时,LiveData中的mVersion的值等于0。

    当调用LiveData进行observe时,我们在上面的源码也分析过,observe最终会走到considerNotify方法:

    // LiveData.java:
    private void considerNotify(ObserverWrapper observer) {
        ......................................
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }
    
    private abstract class ObserverWrapper {
    ......................................
        int mLastVersion = START_VERSION;
    ......................................
    }
    

    可以看到,当observer的mLastVersion小于mVersion时就会把之前的数据回调给监听者。另外,observer中的mLastVersion默认值也为-1,所以在considerNotify中,很明显,mLastVersion小于mVersion(-1 < 0),所以会执行onChanged方法把数据回调给监听者。

    如何解决数据倒灌

    目前解决方案有非常多,这里只说一种,那就是通过反射干预Version值的方式。根据之前的分析,只需要在LiveData绑定owner的时候把Wrapper的version设置成跟LiveData的version一致即可。

    相关文章

      网友评论

        本文标题:LiveData 源码分析

        本文链接:https://www.haomeiwen.com/subject/iepfprtx.html