Android JetPack LiveData源码分析

本文从本人的《一文搞懂Android JetPack组件原理之Lifecycle、LiveData、ViewModel与源码分析技巧》拆分而来,主要采用自顶向下源码分析方法探寻LiveData的原理,原文内容更加全面,欢迎收藏。

组件介绍

LiveData是一个数据持有组件,主要用来通知数据观察者数据发生了更新,它通过与LifecycleOwner组件绑定,实现可以只在页面活跃状态下发起通知,并在页面销毁时自动取消订阅,防止内存泄漏。

下面的简单示例中,直接创建了一个MutableLiveData对象,他持有String类型的数据,通过它的observe()方法,将LifecycleOwner和监听者传入,实现感知生命周期并观察数据的功能。

MutableLiveData<String> liveString = new MutableLiveData<>();
liveString.observe(mOwner, new Observer<String>() {
    @Override
    public void onChanged(@Nullable final String s) {
        Log.d(TAG, "onChanged() called with: s = [" + s + "]");
    }
});

liveString.setValue("LiveData使用案例");

自顶向下

自顶向下分析源码与自底向上在思路上正好相反,需要先绘制出组件的类关系图,然后抽出主要方法,进一步解析方法,从而掌握实现细节,它适合于在对源码组件有一个整体上的了解前提下进行,可以深刻掌握组件的原理特性。接下来就使用该方法总结出LiveData的特性。

第一部分的示例中包含了使用LIveData的基本三部分:定义、注册监听者、更新数据。可以同样以软件过程的输入、处理和输出三个角度为切入点进行分析。处了定义外,监听者相关的操作可以看作是输出,比如注册、回调、注销。更新数据则可以看作是输入,而处理则是在LiveData的内部完成。

类关系图

类关系图

LiveData的类关系图并不复杂,它只有5个类,除了示例中已经出现的有Observer和MutableLiveData,而主要类LiveData则包含和处理的主要逻辑,LiveData的内部类ObserverWrapper和LifecycleBoundObserver则提供了封装Observer和Lifecycle生命周期管理的能力。这里也可以看出LiveData依赖Livecycle的关系。

选择方法

可以从类图中抽出需要分析的方法,回到输入、处理和输出的角度,对应的就是数据更新、数据流转和监听者处理三类方法。

输入-数据更新:postValue()、setValue()、onStateChanged()<br />处理-数据流转:activeStateChanged()、dispatchingValue()、considerNotify()<br />输出-监听者处理:observe()、removeObserver()、onChanged()

接下来,可以使用在源码方法间快速跳转的方式,手动定位并列出相应处理链。
方法间的关系

从该图中可以看出触发数据通知的两个处理链:

  1. 注册监听者后,可以接收到Lifecycle事件,这时候可能会移除监听者,也可能触发了数据通知
  2. 手动postValue或setValue触发数据通知

接下来,便以这两个任务链的顺序,对每个方法进行分析。

observer()注册监听者

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

该方法就是把Lifecycle持有对象LifecycleOwner和一个监听者observer传进来,实现这个监听者在这个生命周期作用域下对LiveData的数据进行监听。这里主要的处理是对observer使用Lifecycle的监听者LifecycleBoundObserver进行了封装,并存入管理所有监听者的mObservers中。这里除了过滤避免重复外,还对监听者对应的LifecycleOwner进行了判断,防止一个监听者处于多个Lifecycle作用域进而导致混乱的情况发生。

LifecycleBoundObserver的onStateChanged()

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

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

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

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
                               @NonNull 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()的逻辑是LifecycleBoundObserver中对接口LifecycleEventObserver的实现,通过对Lifecycle组件的了解,可以知道在LifecycleOwner的生命周期发生改变时,onStateChanged()方法就会被调用到。这里判断如果LifecycleOwner销毁了,那么就移除这个监听者,达到防止内存泄漏的目的,其它情况则会以shouldBeActivie()为值调用activeStateChanged()方法。shouldBeActivie()判断LifecycleOwner的状态是否处于STARTED之后,也就是是否显示在屏幕中,这里表明了LiveData的另一个特性,默认情况下,显示在屏幕中的页面中的监听者才会收到数据更新的通知。

ObserverWrapper的activeStateChanged()

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

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

    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver() {
    }

    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();
        }
        if (mActive) {
            dispatchingValue(this);
        }
    }
}

activeStateChanged()方法是在LifecycleBoundObserver的父类ObserverWrapper中实现的,先看ObserverWrapper的属性,ObserverWrapper不仅封装了监听者,还用mActive管理是否为活跃状态,以及用mLastVersion管理监听者当前的数据版本。回到activeStateChanged()方法,这里的处理主要分三点,首先,用活跃状态是否发生变化做了一个闭路逻辑,防止重复处理,比如onStart()处理后又接收到onResume()。其次,更新当前状态,并判断如果这是第一个监听者活跃,就调用onActive()方法,如果是最后一个监听者非活跃,就调用onInactive()方法。最后,如果是新的活跃状态,则以当前ObserverWrapper对象为参数值调用dispatchingValue()方法分发事件。

setValue()

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

setValue()是LiveData的一个成员方法,用于在主线程中手动更新LiveData中的值,这里先将数据版本mVersion自增后,更新mData的值,并以null为参数值调用dispatchingValue()方法。

postValue()

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};

postValue()也是用来手动更新LiveData中的值的,不过和setValue()有区别的是,它可以在非主线程中调用。它的处理就是在保证线程安全的前提下,通知主线程调用setValue()方法更新数据。

具体细节是,定义一个volatile修饰的成员变量mPandingData,用作线程间共享数据,这个变量的默认值为NOT_SET。通过对共享数据mPandingData的读写访问进行加锁的方式实现线程安全,同时,主线程读取mPandingData的值后,也就被认为是消费掉了共享数据,这时会把mPandingData设置会默认值NOT_SET,而其他线程在拿到锁后写入mPandingData,也就是生产共享数据时,只有之前主线程已消费掉或还未生产过共享数据,才会向主线程发送处理消息。

这个逻辑实现了另外一个特性,当主线程还没来得及处理消息,这时多个线程同时排队拿锁更新数据,主线程最终只会使用最后最新的数据去处理,调用setValue()通知监听者。

dispatchingValue()

dispatchingValue()调用

无论是生命周期回调的activeStateChanged()还是手动发起数据更新setValue(),最终都通过dispatchingValue()完成了数据更新的分发。

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()如果传入的参数不为null,那么就针对该对象单独分发,对应的就是生命周期回调的调用。而如果传入了null,那就遍历mObservers,对每一个监听者完成分发。每次分发是调用considerNotify()完成。

dispatchingValue()的处理中首先使用了两个成员变量mDispatchingValue和mDispatchInvalidated做了一个短路逻辑,这俩成员变量分别表示是否处于分发中和分发的数据是否过期。进入分发过程时,会将mDispatchingValue置为true,mDispatchInvalidated置为false,这时表示处于正常的分发状态。如果在正常分发状态时,再有新的分发请求,那么就会将mDispatchInvalidated值为true,正常的分发状态便会中断,重新开始分发。这就实现了一个特性,只对监听者通知最新的数据。

可以使用下面的单元测试加深对该特性的理解。

@Test
public void testSetValueDuringSetValue() {
    mOwner.handleLifecycleEvent(ON_START);
    final Observer observer1 = spy(new Observer<String>() {
        @Override
        public void onChanged(String o) {
            assertThat(mInObserver, is(false));
            mInObserver = true;
            if (o.equals(("bla"))) {
                mLiveData.setValue("gt");
            }
            mInObserver = false;
        }
    });
    final Observer observer2 = spy(new FailReentranceObserver());
    mLiveData.observe(mOwner, observer1);
    mLiveData.observe(mOwner, observer2);
    mLiveData.setValue("bla");
    verify(observer1, Mockito.atMost(2))
        .onChanged("gt");
    verify(observer2, Mockito.atMost(2))
        .onChanged("gt");
}

这个单元测试在源码库中可以找到,有兴趣的同学可以debug一下看看处理流程,后面会介绍一下这个技巧,这里先简单描述一下代码执行过程。

在这个单元测试中,对于mLiveData有两个监听者observer1和observer2,正常情况下,当mLiveData.setValue("bla")时,dispatchingValue()对监听者进行遍历,两个监听者应该依次收到数据“bla”的通知,但是observer1在收到“bla”后,又执行mLiveData.setValue("gt")发起了新的数据更新,这个第二次dispatchingValue()便会短路,且会中断并重启第一次的遍历,于是observer1会再次收到“gt”,然后才是observer2,它只会收到“gt”。

这个流程便是保证了数据更新只通知最新的,在实际开发中如果遇到setValue()的过程中再次setValue()的情况,就需要特别注意一下这条特性。

considerNotify()

 private void considerNotify(ObserverWrapper observer) {
     if (!observer.mActive) {
         return;
     }
     // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
     //
     // we still first check observer.active to keep it as the entrance for events. So even if
     // the observer moved to an active state, if we've not received that event, we better not
     // notify for a more predictable notification order.
     if (!observer.shouldBeActive()) {
         observer.activeStateChanged(false);
         return;
     }
     if (observer.mLastVersion >= mVersion) {
         return;
     }
     observer.mLastVersion = mVersion;
     observer.mObserver.onChanged((T) mData);
 }

considerNotify()才是最终发起数据更新通知的方法,这里首先检查了监听者及其所处生命周期的活跃状态,并比较了监听者的数据版本和当前数据版本,保证了监听者所在页面处于前台时并且数据版本需要更新时才发起通知。发起通知前会更新监听者的数据版本到最新,确保数据一致。

LiveData特性

分析完这些主要方法后,便可以对LiveData的特性做一个总结了,以便在实际使用过程中更加得心应手。

  1. 一个监听者只能处于一个生命周期作用域中
  2. 监听者通过Lifecycle的特性实现页面销毁后自动注销,防止内存泄漏
  3. 监听者只会在所处的页面在前台的情况下收到数据更新的通知
  4. 由于Lifecycle的特性,监听者如果在所处页面在前台的情况下,注册进LiveData,会立即调用到considerNotify(),这时候如果LiveData的数据版本变化过,便会立即对该监听者发送通知,这也就是所谓的粘性事件。
  5. postValue()经过线程安全处理,最终通过setValue()发起数据更新通知。N次postValue()并不能保证同样N次setValue(),post中防止了重复向主线程发消息,主线程中只会拿当前最新的值调用setValue()。
  6. N次setValue()同样不能保证活跃的监听者被通知到N次,LiveData保证了只通知最新的数据。

总结

自顶向下方法主要难点在于从类关系图中找出要分析的核心方法及其调用关系链,这需要提前对该组件有一定的理解,本次LiveData的分析先使用了软件过程的三个角度框定了方法范围,再通过在源码快速跳转的方式整理出调用链,大家可以在自己分析时参考。确定了要分析的方法后,接下来就是细心的分析工作,需要注意的是在这个过程中要总结出其实现的特性,从而更好地指导在日常实际开发工作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349