Android Jetpack 之 LiveData 源碼探索

源碼 livedata 2.0.0
Jetpack里L(fēng)iveData的相關(guān)類(lèi)不多斤彼,類(lèi)圖見(jiàn)下
[站外圖片上傳中...(image-ec369-1555299590976)]
Observer:作為interface,觀察者卒茬,數(shù)據(jù)發(fā)生改變,通過(guò)onChanged()響應(yīng)改變永品;
LiveData:抽象出來(lái)的統(tǒng)一被觀察者對(duì)象,與Observer建立觀察聯(lián)系的方法是observe()方法击纬。
ComputableLiveData:可計(jì)算的LiveData鼎姐,內(nèi)部持有LiveData,使原數(shù)據(jù)失效并出刷新數(shù)據(jù)更振。
MutableLiveData:這個(gè)方法只是將LiveData的方法權(quán)限修改炕桨,并未實(shí)現(xiàn)其他業(yè)務(wù)。
通常在使用的時(shí)候殃饿,LiveData有可能是由其他類(lèi)或庫(kù)創(chuàng)建出來(lái)的實(shí)例(如Room提供了toLiveData()方法)谋作,也可能是自己 new 出來(lái)的對(duì)象。訂閱見(jiàn)下

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

以正常一次數(shù)據(jù)更新來(lái)看其內(nèi)部實(shí)現(xiàn)乎芳,看數(shù)據(jù)改變的入口遵蚜;
異步更新:mLiveData.postValue(data);
同步更新:mLiveData.setValue(data);

同步setValue()

同步更新方法相對(duì)簡(jiǎn)單,先看看它怎么實(shí)現(xiàn)的奈惑。setValue()方法里首先對(duì)線程進(jìn)行判定吭净,它必須在主線程里面運(yùn)行,之后保存發(fā)更改的數(shù)據(jù)對(duì)象肴甸,并調(diào)用分發(fā)函數(shù)寂殉,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;
    }

內(nèi)部核心considerNotify(),由于這里傳入的是null原在,因此從 mObservers里面獲取數(shù)據(jù)友扰,而LiveData.mObservers對(duì)象則是在observe的時(shí)候添加了數(shù)據(jù)彤叉。

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

最后調(diào)用了onChanged(),完成響應(yīng)回調(diào)村怪。其中有檢測(cè)了觀察者是否是正在觀察等秽浇。同步的代碼流程就完成了。

異步postValue()

異步方法核心是線程切換甚负,懸掛數(shù)據(jù)處理柬焕。
因此,postValue()內(nèi)部必須先對(duì)數(shù)據(jù)進(jìn)行加鎖梭域,判定是不是可以發(fā)送的數(shù)據(jù)斑举。代碼也給了說(shuō)明備注,若在主線程Runnable數(shù)據(jù)時(shí)再連續(xù)postValue()多個(gè)數(shù)據(jù)病涨,最后一個(gè)數(shù)據(jù)才會(huì)被發(fā)送出去富玷。見(jiàn)下,會(huì)直接reutrn .

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

在主線程上將懸掛數(shù)據(jù)發(fā)送出去没宾。看看它是怎么確定在主線程的?
postToMainThread()是虛方法凌彬, ArchTaskExecutor.getInstance()得到的實(shí)例mDefaultTaskExecutor,即DefaultTaskExecutor類(lèi)循衰,它的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铲敛,創(chuàng)建Handdler,并post会钝,因此會(huì)在主線程發(fā)送伐蒋。

再看mPostValueRunnable內(nèi)部是怎么處理的?

  private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
    };

調(diào)用setValue()迁酸,這里已經(jīng)在主線程上了先鱼,因此它是一個(gè)同步發(fā)送事件了,之后的流程就是前面分析的流程奸鬓。

至此焙畔,LiveData的核心代碼就分析完成了。

再看一看具有計(jì)算功能的LiveData串远;

可計(jì)算LiveData (ComputableLiveData)

從功能上來(lái)說(shuō)宏多,使原數(shù)據(jù)失效并出刷新數(shù)據(jù)。
內(nèi)部實(shí)現(xiàn)核心: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);
                }
            }
        }
    };

顯而易見(jiàn)的伸但,它去執(zhí)行的還是mRefreshRunnable,只是將mInvalid標(biāo)志為true, 那看看絕對(duì)核心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());
        }
    };

其邏輯線更胖,如果使用失效,需要調(diào)用compute()去重新計(jì)算新的數(shù)據(jù)源回來(lái),并且在這種情況下會(huì)調(diào)用LiveData的異步更新却妨。
原數(shù)據(jù)失效對(duì)外的方法是invalidate()饵逐。它內(nèi)部將mInvalidationRunnable對(duì)象post到主線程里面。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末管呵,一起剝皮案震驚了整個(gè)濱河市梳毙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捐下,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萌业,死亡現(xiàn)場(chǎng)離奇詭異坷襟,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)生年,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)婴程,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人抱婉,你說(shuō)我怎么就攤上這事档叔。” “怎么了蒸绩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵衙四,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我患亿,道長(zhǎng)传蹈,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任步藕,我火速辦了婚禮惦界,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咙冗。我一直安慰自己沾歪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布雾消。 她就那樣靜靜地躺著灾搏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仪或。 梳的紋絲不亂的頭發(fā)上确镊,一...
    開(kāi)封第一講書(shū)人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音范删,去河邊找鬼蕾域。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旨巷。 我是一名探鬼主播巨缘,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼采呐!你這毒婦竟也來(lái)了若锁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤斧吐,失蹤者是張志新(化名)和其女友劉穎又固,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體煤率,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仰冠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝶糯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洋只。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖昼捍,靈堂內(nèi)的尸體忽然破棺而出识虚,到底是詐尸還是另有隱情,我是刑警寧澤妒茬,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布担锤,位于F島的核電站,受9級(jí)特大地震影響郊闯,放射性物質(zhì)發(fā)生泄漏妻献。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一团赁、第九天 我趴在偏房一處隱蔽的房頂上張望育拨。 院中可真熱鬧,春花似錦欢摄、人聲如沸熬丧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)析蝴。三九已至,卻和暖如春绿淋,著一層夾襖步出監(jiān)牢的瞬間闷畸,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工吞滞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佑菩,地道東北人盾沫。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像殿漠,于是被迫代替她去往敵國(guó)和親赴精。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 女兒前兩天有些感冒绞幌,本以為這兩天會(huì)康復(fù)蕾哟,沒(méi)想到因?yàn)樘鞖庾兓俅伟l(fā)展到嗓子發(fā)炎莲蜘,今天出現(xiàn)發(fā)燒的情況谭确。 每個(gè)冬天都會(huì)...
    阿略1閱讀 1,543評(píng)論 47 59
  • 我是一名心理咨詢(xún)師琼富,因?yàn)閼賽?ài)婚姻問(wèn)題前來(lái)咨詢(xún)的人很多。 [if !supportLists](1)[endif]“...
    正向聚焦父母課堂閱讀 525評(píng)論 0 0
  • 周末的美好與幸福往往彰顯了周一的憂郁庄新,不知有多少人與我有同感?妞最近追劇《剃刀邊緣》薯鼠,說(shuō)是妞爸推薦择诈,我算服了這父女...
    微塵ht閱讀 138評(píng)論 0 1