Jetpack源码解析(二):LiveData解析

JetPack作为Google官方推荐的一套标准化开发套件,很值得去使用和学习。

这篇介绍LiveDataLiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

基本使用

LiveData本质是一个①遵循生命周期、②可供观察的存储类。

使用姿势1 使用姿势2
正常使用.png
黏性使用.png

乍一看和我们之前学的EventBusRxBus之类的事件总线很像,不就是个观察者模式嘛。姿势2不就是反映出黏性的状态嘛。

事实也是如此。

不过它具有生命周期感知能力LiveData 只会将更新通知给活跃的观察者。为观察 LiveData对象而注册的非活跃观察者不会收到更改通知。

Google官方不建议我们这样直接在Activity中使用,而是将LiveData放在ViewModle中,Activity中只负责监听及变化后的处理事件。以构建成MVVM架构。

源码解析

image-20201218114150694.png

MutableLiveData继承自LiveData,直接看LiveDataobserve()方法。

observe(owner,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);
}

之前说过,LiveData 仅更新处于活跃生命周期状态的应用组件观察者,这里的活跃状态具体指的是STARTRESUME

首先是必须要在主线程执行,判断生命周期状态是否活跃;然后new一个LifecycleBoundObserver命名为wrapper,将其作为VALUE,以传进来的observer为KEY,存入mObservers中。

然后将wrapper放入生命周期的监听中。wrapper实现了LifecycleEventObserver接口,所以wrapper放进去后接着就会调用wrapper中的onStateChanged方法【这一句内容属于Lifecycle知识点,详见Lifecycle解析】。

wrapper.onStateChanged

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

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

判断了下生命周期是否活跃,然后走向了activeStateChanged( )

这里传入的布尔值是判断生命周期状态:当前状态在STARTED之前,false; 在STARTED状态之后,true。

说明只有在STARTEDRESUME状态时会传true进去,印证了之前说的 “活跃状态具体指的是STARTRESUME

//这个方法是在抽象类`ObserverWrapper`中。
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);
    }
}

这个方法中,先将传进来的newActive赋值给mActive

mActiveCount表示活跃状态下 obs的数量;wasInactive(为true)表示之前活跃状态下没有obs;

mActivetrue表示当下处于活跃状态,并且你传给了我个obs。

所以mActive为true的时候,mActiveCount要+1;为false时,mActiveCount要-1。注意这里mActiveCount发生了改变!!

然后判断,如果改变之前没有obs活跃 && 传来的obs即将变活跃,那就要onActive();

如果 改变之后依然没有obs活跃 && 传来的obs也不会变活跃,那就onInactive();

当然这两个方法都是空的,需要用户自己去写。

说人话就是:当活跃的数量0-->1时,执行onActive();由1-->0时,执行onInactive()

最重要的其实是下一句:如果现在是活跃状态,我收到了obs,所以要执行下一步操作了。

dispatchingValue(wrapper)

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;
}

这两个布尔值,emmm,看的有点绕。再看这个do while,这循环怎么这么怪?

//第一次A,B = false
 if(A){
     B = true;
     return
 }
A = true
do{
    B = false
        ...//不会改变B的代码
}while(B)

WTF??一脸黑人问号?这种循环能跑到第二圈?这种循环要是能循起来,我当场把显示器吃掉!!

不管了,重点看do while中的代码,传进来wrapper是不为空的,走considerNotify方法,

private void considerNotify(ObserverWrapper observer) {
    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);
}

一些列判断之后,如果未被return,则执行wrapper.mObserver.onChanged()方法。

当然,第一次 observer.mLastVersionmVersion都是初始值-1,所以不会执行到此。除非mVersion变了...

大的流程走通了,再看看setValue方法。

setValue

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

版本++,存储data,然后再dispatchingValue(null);

等等,这里也进到了dispatchingValue,也就是说dispatchingValue中可能出现多个线程同时访问进去的情况。这样一来之前看不懂的那两个布尔值和一个长得奇怪的do while循环就说得过去了。

Snipaste_2020-12-18_16-36-26.png

这种情况下,循环就循环起来了。

image-20201218170327040.png

咳咳,我们继续😂。dispatchingValue方法中有一个针对null的判断,这时候传进来的initiator为空,走else的方法。把mObservers遍历一遍,然后依次执行considerNotify(iterator.next().getValue());

如果大家记性不太好的话,我帮大家回忆一下:

LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

所以getValue方法中得到的就是我们在observe方法中初始化的wrapper。

再看considerNotify

private void considerNotify(ObserverWrapper observer) {
    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);
}

这次进入这个方法的时候有一点不一样,我们在setValue方法中版本++了,所以这个时候mVersion大于mLastVersion了,这个时候才会执行observer.onChanged方法。

mVersion的意义就在这里体现出来了。

如果你先setValue(版本++),然后再observer(),onChanged依然会被触发。这就是所谓的黏性事件。也就是说黏性是在源码中写死了的,不支持像EventBus那般可进行配置。你想不黏性都不行。

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() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        setValue((T) newValue);
    }
};

postValue也一样,通过一个mPostValueRunnable,最后转到了SetValue方法。

总结

LiveData的作用其实是事件的分发,其实现原理是基于发布/订阅者模式,但是LiveData和传统的EventBus、RxBus这类事件总线又不太一样。

Google对其在功能上加以限制,几乎只有set/post和observe这三个方法;而上述Bus框架理论上可以在代码的任意一处进行分发,在代码的其他地方进行任意调用,虽然灵活,但无形中增加了管理成本,尤其是当项目日益庞大的时候,若项目中的Bus管理不善,发送和接收的事件(event)将会泛滥成灾,造成不可预期的后果。

而LiveData,可以通过和ViewModle的配合,完美规避这些问题,使事件的传递更加纯粹 和 易于管理。

同时,LiveData生命周期感知能力也避免了由于生命周期的变化带来的内存泄露等问题。

当然,你也完全可以利用LiveData将其封装成LiveDataBus,实现相似的事件总线框架,但是这有违Google的初衷,Google开发团队的初衷是让我们使用LiveData+ViewModle的模式来管理数据,这才是Google团队认为的最佳做法。


最后,关于LiveData的粘性设计可能带来的BUG和解决方案,我会另开一篇文章详谈,不在此赘述。

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

推荐阅读更多精彩内容