【Jetpack篇】LiveData取代EventBus弟胀?LiveData的通信原理和粘性事件刨析

LiveData是一個可以在給定生命周期內(nèi)觀察到的數(shù)據(jù)持有者類。一個觀察者可以與一個LifecycleOwner成對地添加,并且只有當(dāng)配對的LifecycleOwner處于活動狀態(tài)時邮利,這個觀察者才會收到數(shù)據(jù)變動的通知。

LiveData-ViewModel-Repository

以下是LiveData結(jié)合ViewModel請求數(shù)據(jù)的偽代碼垃帅,這也是JetPack中最常見的一種寫法延届。

ViewModel層:繼承ViewModel并創(chuàng)建LiveData,通過postValue將數(shù)據(jù)發(fā)射給UI贸诚。

class ExViewModel() : ViewModel() {
    private lateinit var repo: ExRepo
    val liveData: MutableLiveData<String> = MutableLiveData()
    //調(diào)用postValue通知UI數(shù)據(jù)被改變
    fun loadData() = liveData.postValue(repo.loadDataFromRepo())
}

Repository層:從網(wǎng)絡(luò)或者數(shù)據(jù)庫中請求數(shù)據(jù)方庭。

class ExRepo() {
    //網(wǎng)絡(luò)請求或者從數(shù)據(jù)庫中請求
    fun loadDataFromRepo(): String = "網(wǎng)絡(luò)請求返回的數(shù)據(jù)"
}

UI層:注冊LiveData的觀察者,接收數(shù)據(jù)變化的通知來更新UI

class ExFragment: Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val viewModel = ViewModelProviders.of(this).get(ExViewModel::class.java)
        //注冊觀察者
        viewModel.liveData.observe(this, object:Observer<String> {
            override fun onChanged(value: String) {
                   //請求數(shù)據(jù)成功后酱固,通知UI刷新界面
                   updateUI()
            }
        })

        //請求數(shù)據(jù)
        viewModel.loadData()
    }
}

上面三層基本上就是一個網(wǎng)絡(luò)請求的閉環(huán)械念,Repository負(fù)責(zé)從網(wǎng)絡(luò)或者數(shù)據(jù)庫中請求數(shù)據(jù),ViewModel負(fù)責(zé)調(diào)用Repository的數(shù)據(jù)运悲,并利用LiveData的postValue()/setValue()方法將數(shù)據(jù)的更新通知到UI層龄减,UI則只需要注冊一個LiveData的觀察者,當(dāng)所匹配的LiveData調(diào)用postValue()/setValue()時班眯,就會收到onChange()的通知希停,去做更新UI的操作。

image.png

(此圖來源網(wǎng)上)

LiveData遵循觀察者模式署隘,它的厲害之處不僅在于數(shù)據(jù)有變化時能及時通知UI宠能,而且LiveData能夠感知Activity、Fragment等組件的生命周期磁餐,隨組件銷毀而自動銷毀违崇,不用開發(fā)者去操心,極大的減少了內(nèi)存泄漏的可能诊霹。

那LiveData是如何通信并且感知組件的生命周期的呢羞延?

LiveData的通信原理

從上面例子可以知道LiveData的核心主要在于這兩步,liveData.observe()以及l(fā)iveData.postValue()畅哑,一個是注冊觀察者肴楷,一個是發(fā)送通知。那么下面的解析就將這兩個函數(shù)作為切入點(diǎn)荠呐。

1.LiveData.observe()

從liveData.observe()跟蹤進(jìn)去:

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");
        //? 第一部分
        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);
    }

observe方法傳有兩個參數(shù)LifecycleOwner和Observer赛蔫,LifecycleOwner是一個具有Android生命周期的類,一般傳入的是Activity和Fragment泥张,Observer是一個接口呵恢,內(nèi)部存在void onChanged(T t)方法。

? 第一部分: observe內(nèi)部一開始就存在一個生命周期的判斷媚创,

if (owner.getLifecycle().getCurrentState() == DESTROYED) {return;}

當(dāng)組件生命周期已經(jīng)Destroy了渗钉,也就沒有必要再繼續(xù)走下去,則直接return。在這里鳄橘,LiveData對生命周期的感知也就慢慢顯現(xiàn)出來了声离。

? 第二部分: 首先以LifecycleOwner和Observer作為參數(shù)創(chuàng)建了一個LifecycleBoundObserver對象,接著以O(shè)bserver為key瘫怜,新創(chuàng)建的LifecycleBoundObserver為value术徊,存儲到mObservers這個map中。在后面LiveData postValue中會遍歷出該map的value值ObserverWrapper鲸湃,獲取組件生命周期的狀態(tài)赠涮,已此狀態(tài)來決定分不分發(fā)通知(這部分詳情見“第二小節(jié)postValue()”)

那LifecycleBoundObserver是什么?

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

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

        .......
    }

從源碼可以看到暗挑,LifecycleBoundObserver繼承ObserverWrapper并且實(shí)現(xiàn)了LifecycleEventObserver的接口笋除,LifecycleEventObserver是監(jiān)聽組件生命周期更改并將其分派給接收方的一個接口,而在LifecycleBoundObserver的構(gòu)造函數(shù)中將observer傳給了父類ObserverWrapper炸裆。LifecycleBoundObserver其實(shí)只是包裹著LifecycleOwner和Observer得一個類垃它,其中的實(shí)現(xiàn)有點(diǎn)代理模式的味道晒衩。

? 第三部分: owner.getLifecycle().addObserver(wrapper)將新創(chuàng)建的LifecycleBoundObserver添加到Lifecycle中听系,也就是說這個時候觀察者注冊成功,當(dāng)LifecycleOwner也就是組件的狀態(tài)發(fā)生改變時,也會通知到所匹配的observer。

到這里损合,UI層viewModel.liveData.observe(this, object:Observer<String> { override fun onChanged(value: String) {} })注冊觀察者的內(nèi)部解析也就大致清楚了。

2.postValue()

liveData.postValue()是作為一個發(fā)射方來通知數(shù)據(jù)改變,其內(nèi)部又做了哪些工作纠修?接下來就一探究竟吁系。直接從postValue中最核心的部分在于將參數(shù)value賦值給了一個全局變量源碼開始:

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

postValue中首先將參數(shù)value賦值給了一個全局變量mPendingData福荸,它的初始值為一個空對象背传,而mPendingData只是作為一個中間媒介來存儲value的值,在后續(xù)的操作中會用到,我們就暫時先記住它冤灾。

在最后就是一個將線程切換到主線程的操作移宅,主要看mPostValueRunnable的實(shí)現(xiàn):

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

在Runnable中,mPendingData賦值給了臨時變量newValue,最后調(diào)用了setValue()方法。我們都知道LiveData發(fā)送通知可以使用PostValue或者SetValue攻晒,而他兩的區(qū)別就在于,PostValue可以在任意線程中調(diào)用,而SetValue只能在主線程中,因?yàn)镻ostValue多了一步上面切換主線程的操作。

OK,接下來就是PostValue/SetValue最核心的部分。


    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        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;
    }

在setValue中贾虽,參數(shù)value將值賦給了一個全局變量mData,而這個mData最后將通過mObserver.onChanged((T) mData);將需要修改的value值分發(fā)給了UI蓬豁。最后調(diào)用傳入一個null調(diào)用dispatchingValue方法绰咽。

由于dispatchingValue里的參數(shù)為null,也就順理成章的走到了? 第二部分地粪。else一進(jìn)入就是迭代器在遍歷mObservers取募,而mObservers在第一小節(jié)“1.LiveData.observe()”中說得很清楚,它作為一個map蟆技,存儲了Observer和ObserverWrapper玩敏。通過遍歷斗忌,將每個觀察者所匹配的ObserverWrapper作為參數(shù)傳給了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()中旺聚,先通過observer來獲取組件生命周期的狀態(tài)织阳,如果處于非活動狀態(tài),則拒絕發(fā)起通知砰粹。在該方法的最后唧躲, observer.mObserver.onChanged((T) mData),是不是很熟悉碱璃,這就是UI層一開始就實(shí)現(xiàn)的接口弄痹,而就在這找到了最后的發(fā)送方。

小結(jié)

LiveData是如何通信的嵌器?就一句話界酒,UI層注冊好一個observer,就存儲到一個存儲著觀察者的map中嘴秸,直到開發(fā)者調(diào)用postValue/setValue則遍歷該map,分發(fā)出observer的onChanged通知庇谆,在此過程中岳掐,都會監(jiān)聽組件的生命周期,并以此來判斷所匹配的組件是否處于活動狀態(tài)饭耳,否則直接return串述。

三、LiveData的粘性事件

發(fā)送消息事件早于注冊事件寞肖,依然能夠接收到消息的通知的為粘性事件

即先調(diào)用LiveData的postValue/setValue方法纲酗,后注冊observe,依然能夠收到onChange()的通知新蟆。從上面的分析可以知道觅赊,LiveData最后postValue是將通知分發(fā)給已經(jīng)注冊好的觀察者,而LiveData的粘性事件是先發(fā)送后注冊琼稻,那為什么也能夠收到通知呢吮螺?是哪里分發(fā)了onChange()?

我們知道粘性事件是注冊后就收到了通知,那么就可以以liveData.observe()為切入點(diǎn)帕翻,康康源碼中的實(shí)現(xiàn)鸠补。

 @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        ......
        owner.getLifecycle().addObserver(wrapper);
    }

LiveData注冊觀察者,最核心在于owner.getLifecycle().addObserver(wrapper);addObserver是用來添加一個LifecycleObserver嘀掸,當(dāng)LifecycleOwner改變狀態(tài)時紫岩,它會被通知。例如睬塌,如果LifecycleOwner處于State#STARTED狀態(tài)泉蝌,給定的觀察者將收到Event#ON_CREATE歇万、Event#ON_START事件。

而我們跟蹤到它的實(shí)現(xiàn)類里面梨与,

public class LifecycleRegistry extends Lifecycle {
......
@Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

        if (previous != null) {
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            // it is null we should be destroyed. Fallback quickly
            return;
        }

        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            pushParentState(statefulObserver.mState);
            ? 切入點(diǎn)
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }

       ......
    }
}

LifecycleRegistry繼承了Lifecycle堕花,是一個處理多個觀察者的類,在這個類中的addObserver方法有一個被我標(biāo)記?切入點(diǎn)
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState))粥鞋,dispatchEvent表示將ON_CREATE缘挽、ON_START等事件分發(fā)給觀察者,以達(dá)到監(jiān)聽生命周期的作用呻粹。在dispatchEvent方法中壕曼,LifecycleEventObserver通過onStateChanged接口分發(fā)給了相應(yīng)的觀察者,如下:

 void dispatchEvent(LifecycleOwner owner, Event event) {
            State newState = getStateAfter(event);
            mState = min(mState, newState);
            ? 切入點(diǎn)
            mLifecycleObserver.onStateChanged(owner, event);
            mState = newState;
        }

看到mLifecycleObserver.onStateChanged(owner, event)是不是有種熟悉的感覺等浊,其實(shí)在第二節(jié)源碼分析中腮郊,類LifecycleBoundObserver就實(shí)現(xiàn)了LifecycleEventObserver接口,并重寫了onStateChanged方法筹燕。

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @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;
            }
            ? 切入點(diǎn)
            activeStateChanged(shouldBeActive());
        }

     ......
    }

如果當(dāng)前生命周期狀態(tài)處于DESTROYED轧飞,則將觀察者從列表中移除,防止內(nèi)存泄漏撒踪,接著就調(diào)用了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();
            }
            ? 切入點(diǎn)
            if (mActive) {
                dispatchingValue(this);
            }
        }

在 ? 切入點(diǎn)之前代碼不需要多看,只是一些判斷當(dāng)前活動是否活躍的一起邏輯制妄,核心在于dispatchingValue(this)掸绞,看到這個方法,是不是又感覺到一絲熟悉耕捞,如果看了第二節(jié)的分析衔掸,就會發(fā)現(xiàn)第二節(jié)setValue()中也調(diào)用了一句dispatchingValue(null),只不過setValue傳入的是null,而現(xiàn)在這里傳入了一個this值俺抽。接下啦就來看看傳如參數(shù)和null有什么不同敞映?

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        ......
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                ?切入點(diǎn)1
                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;
    }

因?yàn)閐ispatchingValue傳入的參數(shù)不為null,則邏輯就走進(jìn)了 ?切入點(diǎn)1中磷斧,接著將initiator作為參數(shù)驱显,調(diào)用了considerNotify方法,這個其實(shí)和setValue最后調(diào)用的方法是一致的瞳抓,只是傳入的參數(shù)不同罷了埃疫。considerNotify方法中最后將數(shù)據(jù)發(fā)送給了觀察者。

  private void considerNotify(ObserverWrapper observer) {
        ......
        observer.mObserver.onChanged((T) mData);
    }

到此孩哑,整個流程也就梳理完成栓霜。

小結(jié)

LiveData的粘性事件,在于LiveData只要在注冊觀察者時横蜒,就會觸發(fā)LifecycleOwner的狀態(tài)改變胳蛮,并且會將ON_CREATE,ON_START,ON_RESUME三種狀態(tài)分發(fā)給觀察者销凑,而這,就觸發(fā)了onChange的通知仅炊。

image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斗幼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子抚垄,更是在濱河造成了極大的恐慌蜕窿,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呆馁,死亡現(xiàn)場離奇詭異桐经,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)浙滤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門阴挣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纺腊,你說我怎么就攤上這事畔咧。” “怎么了揖膜?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵誓沸,是天一觀的道長。 經(jīng)常有香客問我次氨,道長,這世上最難降的妖魔是什么摘投? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任煮寡,我火速辦了婚禮,結(jié)果婚禮上犀呼,老公的妹妹穿的比我還像新娘幸撕。我一直安慰自己,他們只是感情好外臂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布坐儿。 她就那樣靜靜地躺著,像睡著了一般宋光。 火紅的嫁衣襯著肌膚如雪貌矿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天罪佳,我揣著相機(jī)與錄音逛漫,去河邊找鬼。 笑死赘艳,一個胖子當(dāng)著我的面吹牛酌毡,可吹牛的內(nèi)容都是我干的克握。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼枷踏,長吁一口氣:“原來是場噩夢啊……” “哼菩暗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旭蠕,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤员咽,失蹤者是張志新(化名)和其女友劉穎搓扯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡遣总,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辜腺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宣鄙。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖岗屏,靈堂內(nèi)的尸體忽然破棺而出辆琅,到底是詐尸還是另有隱情,我是刑警寧澤这刷,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布婉烟,位于F島的核電站,受9級特大地震影響暇屋,放射性物質(zhì)發(fā)生泄漏似袁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一咐刨、第九天 我趴在偏房一處隱蔽的房頂上張望昙衅。 院中可真熱鬧,春花似錦定鸟、人聲如沸而涉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啼县。三九已至,卻和暖如春沸久,著一層夾襖步出監(jiān)牢的瞬間季眷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工卷胯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘟裸,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓诵竭,卻偏偏與公主長得像话告,于是被迫代替她去往敵國和親兼搏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348

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