Android Jetpack 之 LiveData 源码探索

源码 livedata 2.0.0
Jetpack里LiveData的相关类不多,类图见下
[站外图片上传中...(image-ec369-1555299590976)]
Observer:作为interface,观察者,数据发生改变,通过onChanged()响应改变;
LiveData:抽象出来的统一被观察者对象,与Observer建立观察联系的方法是observe()方法。
ComputableLiveData:可计算的LiveData,内部持有LiveData,使原数据失效并出刷新数据。
MutableLiveData:这个方法只是将LiveData的方法权限修改,并未实现其他业务。
通常在使用的时候,LiveData有可能是由其他类或库创建出来的实例(如Room提供了toLiveData()方法),也可能是自己 new 出来的对象。订阅见下

mLiveData.observe(Activity.this,Observer(){
  Log.d(TAG, it.toString());
})

以正常一次数据更新来看其内部实现,看数据改变的入口;
异步更新:mLiveData.postValue(data);
同步更新:mLiveData.setValue(data);

同步setValue()

同步更新方法相对简单,先看看它怎么实现的。setValue()方法里首先对线程进行判定,它必须在主线程里面运行,之后保存发更改的数据对象,并调用分发函数,dispatchingValue(null);

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

内部核心considerNotify(),由于这里传入的是null,因此从 mObservers里面获取数据,而LiveData.mObservers对象则是在observe的时候添加了数据。

    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;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }

最后调用了onChanged(),完成响应回调。其中有检测了观察者是否是正在观察等。同步的代码流程就完成了。

异步postValue()

异步方法核心是线程切换,悬挂数据处理。
因此,postValue()内部必须先对数据进行加锁,判定是不是可以发送的数据。代码也给了说明备注,若在主线程Runnable数据时再连续postValue()多个数据,最后一个数据才会被发送出去。见下,会直接reutrn .

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

在主线程上将悬挂数据发送出去。看看它是怎么确定在主线程的?
postToMainThread()是虚方法, ArchTaskExecutor.getInstance()得到的实例mDefaultTaskExecutor,即DefaultTaskExecutor类,它的postToMainThread()如下:

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

获取到主线程的Looper,创建Handdler,并post,因此会在主线程发送。

再看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);
        }
    };

调用setValue(),这里已经在主线程上了,因此它是一个同步发送事件了,之后的流程就是前面分析的流程。

至此,LiveData的核心代码就分析完成了。

再看一看具有计算功能的LiveData;

可计算LiveData (ComputableLiveData)

从功能上来说,使原数据失效并出刷新数据。
内部实现核心:mRefreshRunnable、mInvalidationRunnable
先看刷新mInvalidationRunnable

  final Runnable mInvalidationRunnable = new Runnable() {
        @MainThread
        @Override
        public void run() {
            boolean isActive = mLiveData.hasActiveObservers();
            if (mInvalid.compareAndSet(false, true)) {
                if (isActive) {
                    mExecutor.execute(mRefreshRunnable);
                }
            }
        }
    };

显而易见的,它去执行的还是mRefreshRunnable,只是将mInvalid标志为true, 那看看绝对核心mRefreshRunnable,

 @VisibleForTesting
    final Runnable mRefreshRunnable = new Runnable() {
        @WorkerThread
        @Override
        public void run() {
            boolean computed;
            do {
                computed = false;
                // compute can happen only in 1 thread but no reason to lock others.
                if (mComputing.compareAndSet(false, true)) {
                    // as long as it is invalid, keep computing.
                    try {
                        T value = null;
                        while (mInvalid.compareAndSet(true, false)) {
                            computed = true;
                            value = compute();
                        }
                        if (computed) {
                            mLiveData.postValue(value);
                        }
                    } finally {
                        // release compute lock
                        mComputing.set(false);
                    }
                }
                // check invalid after releasing compute lock to avoid the following scenario.
                // Thread A runs compute()
                // Thread A checks invalid, it is false
                // Main thread sets invalid to true
                // Thread B runs, fails to acquire compute lock and skips
                // Thread A releases compute lock
                // We've left invalid in set state. The check below recovers.
            } while (computed && mInvalid.get());
        }
    };

其逻辑线,如果使用失效,需要调用compute()去重新计算新的数据源回来,并且在这种情况下会调用LiveData的异步更新。
原数据失效对外的方法是invalidate()。它内部将mInvalidationRunnable对象post到主线程里面。

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

推荐阅读更多精彩内容

  • 女儿前两天有些感冒,本以为这两天会康复,没想到因为天气变化,再次发展到嗓子发炎,今天出现发烧的情况。 每个冬天都会...
    阿略1阅读 1,540评论 47 59
  • 我是一名心理咨询师,因为恋爱婚姻问题前来咨询的人很多。 [if !supportLists](1)[endif]“...
    正向聚焦父母课堂阅读 504评论 0 0
  • 周末的美好与幸福往往彰显了周一的忧郁,不知有多少人与我有同感?妞最近追剧《剃刀边缘》,说是妞爸推荐,我算服了这父女...
    微尘ht阅读 131评论 0 1