Android不使用反射波势,完成LiveDataBus

LiveDataBus大家都很熟悉了翎朱,網(wǎng)上也有很多通過反射實(shí)現(xiàn)的LiveDataBus。但是通過反射實(shí)現(xiàn)的代碼比較混亂尺铣,也比較難以理解拴曲。這里給出一版通過代碼實(shí)現(xiàn)的。更加的簡(jiǎn)潔優(yōu)雅~

首先來(lái)看一下LiveData原理

一般我們都是這樣使用的凛忿,創(chuàng)建一個(gè)LiveData去發(fā)送數(shù)據(jù)澈灼,在你想觀察的地方去注冊(cè)。這樣只要數(shù)據(jù)發(fā)射侄非,你就能拿到你想要的數(shù)據(jù)了蕉汪。


image.png

下面就是你再使用紅框語(yǔ)句時(shí)的調(diào)用流程


image.png

所以首先進(jìn)入 observe 方法看一看

image.png

這樣我們創(chuàng)建的LifecycleBoundObserver(observe方法中的new Observer
就和宿主(observe方法中傳入的this) 建立了聯(lián)系。

所以宿主每次生命周期的變化都會(huì)調(diào)用到
LifecycleBoundObserver的onStateChanged
而從代碼中也可以看到逞怨,在宿主生命周期是DESTROYED時(shí)者疤,會(huì)主動(dòng)移除掉當(dāng)前mObserver,完成自動(dòng)反注冊(cè)叠赦,這里注意要把mObservers 和 mObserver分清楚

image.png

這里有幾點(diǎn)需要注意一下
1.LiveData中的mObservers是一個(gè)Map驹马,還有一個(gè)mVersion字段默認(rèn)等于 -1


image.png

2.LifecycleBoundObserver 繼承 ObserverWrapper
里面有mObserver,其實(shí)就是保存自己
還有一個(gè)mLastVersion字段除秀,默認(rèn)等于 -1


image.png

接下來(lái)繼續(xù)進(jìn)入activeStateChanged方法糯累,其他方法不多解釋。
這里直接進(jìn)入dispatchingValue
可以看到dispatchingValue不管走哪邊都會(huì)進(jìn)入considerNotify


image.png
image.png

接下來(lái)看considerNotify


image.png

看到這里我相信你已經(jīng)知道册踩,我們?yōu)樯赌茉賝nChanged拿到數(shù)據(jù)了

    viewModel.liveDataObject.observe(this, new Observer<Bean>() {
        @Override
        public void onChanged(Bean data) {
              接收數(shù)據(jù)
        }
    });

接下來(lái)看一下泳姐,postValue和setValue

可以看到setValue是有注解MainThread的,表示只能在主線程中使用
而postValue沒有暂吉,把某事件拋到主線程去了


image.png

再來(lái)來(lái)看一下胖秒,postValue在切換到主線程中都干了些啥,我們發(fā)現(xiàn)他的Runnable中的方法慕的,最終還是執(zhí)行了setValue阎肝。


image.png

所以這樣看
postValue只不過是可以在子線程執(zhí)行,但是消息發(fā)送最終還是要到主線程肮街,且執(zhí)行setValue
而setValue就只能在主線程執(zhí)行了

在執(zhí)行了setValue或者postValue后风题,mVersion+1,接著直接進(jìn)入到considerNotify


image.png

黏性事件怎么來(lái)的嫉父?

為了造成黏性事件沛硅,我再注冊(cè)觀察者之前就將數(shù)據(jù)發(fā)送出去,然后通過按鈕點(diǎn)擊再去注冊(cè)一個(gè)觀察者熔号,我們能發(fā)現(xiàn)稽鞭,即使是之前發(fā)送的數(shù)據(jù),仍然能夠接受得到引镊,這就是黏性事件朦蕴。


image.png

造成的原因就是mLastVersion 和 mVersion

image.png

實(shí)現(xiàn)自己的LiveDataBus

LiveData基本的都了解過了,接下來(lái)自己實(shí)現(xiàn)一個(gè)弟头,既可以接受黏性事件吩抓,又可以接受普通事件。

怎么控制黏性事件

其實(shí)原本的代碼就是可以發(fā)送事件的赴恨,只不過不能自由的控制黏性事件疹娶,
**如果我們能用一個(gè)變量去標(biāo)志就好了,比如這樣標(biāo)志一個(gè)receiveSticky變量**
為true就是接受黏性事件伦连,那么調(diào)用方法發(fā)送數(shù)據(jù)
為false的話就會(huì)雨饺,跳過此方法


if (observer.mLastVersion >= mVersion) { 
     return;
}
observer.mLastVersion = mVersion;
if(receiveSticky){
     observer.mObserver.onChanged((T) mData);
} else {
     // 處理普通事件
}

在假設(shè)我們能直接在源碼添加這個(gè)字段的話钳垮,那這個(gè)receiveSticky從哪里來(lái)呢?
1.發(fā)送者的角度:從 postValue 和 setValue 入手
比如改寫成 postValue(data,receiveSticky)
這樣有個(gè)弊端额港,這樣只能統(tǒng)一發(fā)送黏性或者非黏性饺窿,這樣如果多個(gè)宿主監(jiān)聽同一個(gè)消息,而有些需要黏性移斩,有些不需要肚医,這樣就很難控制

2.從接收者的角度:從observer入手
我們知道我們傳入的observer,在包裝成LifecycleBoundObserver后向瓷,才有mLastVersion肠套。那我們可以參考一下這種思路

比如:LifecycleBoundObserver包裝一下,有了mLastVersion
那么:我們將傳入的Observer也包裝一層猖任,在創(chuàng)建的時(shí)候傳入receiveSticky就好了
就像這樣:(當(dāng)然這不是完整版你稚,這只是記錄一下思路)

image.png

怎么保證接收的是同一個(gè)事件

        liveData.postValue
        viewModel.liveData.observer(this , Observer {

        })

一般我們都是這樣發(fā)送接收的,這個(gè)LiveData都是同一個(gè)才能接收同一份數(shù)據(jù)朱躺。
所以我們也必須在LiveDataBus保證是同一個(gè)LiveData才行入宦。

還是同樣的思路,要區(qū)分LiveData室琢,就給LiveData加名字就行了唄
那我就再給LiveData包裝一層乾闰,讓調(diào)用者傳入名字去生成
生成完了就保存下來(lái),以后就用名字去找到對(duì)應(yīng)的LiveData
就好比:

image.png

那么他的用法就是這樣的:接收消息的有點(diǎn)過于復(fù)雜了盈滴。


image.png

既然observer是LiveData里面的方法涯肩,
而每次發(fā)送消息時(shí)間都是StickyObserver(sticky, Observer())
這樣我們就可以在我們的包裝類中去復(fù)寫一下observer,比如:

image.png

這樣發(fā)送接收數(shù)據(jù)就會(huì)變成巢钓,比之前稍微好一些


image.png

如何解決普通事件的接收

在上面我們其實(shí)沒對(duì)普通事件做處理
我們通過sticky能判斷接不接受黏性事件
但是我們不知道在我們注冊(cè)之前病苗,有沒有消息事件發(fā)送

override fun onChanged(t: T) {
    if (sticky) {
       observer.onChanged(t)
    } else {
        // 普通事件
    }
}

回想一下,黏性事件是怎么產(chǎn)生的症汹?
簡(jiǎn)單的認(rèn)為硫朦,observer.mLastVersion(Observer的) < mVersion(LiveData的) 就會(huì)產(chǎn)生黏性事件
所以我們也可以模仿一下,弄兩個(gè)變量去判斷


image.png

在我們的LiveDate中


image.png

在我們的observe中會(huì)被改寫成這樣


image.png

所以顯然不能完全完全按照源碼照抄
那黏性事件之所以會(huì)被發(fā)送出去背镇,
就是在StickyObserver初始化時(shí)mLastVersionmLiveDataVersion沒對(duì)齊咬展,
導(dǎo)致if (mLastVersion >= stickyLiveData.mLiveDataVersion) {} 沒進(jìn)入
所以進(jìn)入if條件就有黏性事件,所以我們要改成這樣

image.png

最后附上整個(gè)代碼瞒斩,直接復(fù)制就可以用


object LiveDataBus {

//    LiveDataBus.with<String>("TestLiveDataBus").postStickyData("測(cè)試破婆!")
//    LiveDataBus.with<String>("TestLiveDataBus") .observerSticky(this, false) {
//
//    }

    private val mStickyMap = ConcurrentHashMap<String, StickyLiveData<*>>()

    fun <T> with(eventName: String): StickyLiveData<T> {
        var stickyLiveData = mStickyMap[eventName]
        if (stickyLiveData == null) {
            stickyLiveData = StickyLiveData<T>(eventName)
            mStickyMap[eventName] = stickyLiveData
        }

        return stickyLiveData as StickyLiveData<T>
    }


    /**
     * 將發(fā)射出去的LiveData包裝一下,再做一些數(shù)據(jù)保存
     */
    class StickyLiveData<T>(private var eventName: String) : LiveData<T>() {

        var mLiveDataVersion = 0
        var mStickyData: T? = null

        fun setStickyData(stickyData: T) {
            mStickyData = stickyData
            setValue(stickyData)
        }

        fun postStickyData(stickyData: T) {
            mStickyData = stickyData
            postValue(stickyData)
        }

        override fun setValue(value: T) {
            mLiveDataVersion++
            super.setValue(value)
        }

        override fun postValue(value: T) {
            super.postValue(value)
        }

        fun observerSticky(owner: LifecycleOwner, sticky: Boolean, observer: Observer<in T>) {
            // 移除自己保存的StickyLiveData
            owner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
                if (event == Lifecycle.Event.ON_DESTROY) {
                    mStickyMap.remove(eventName)
                }
            })

            super.observe(owner, StickyObserver(this, sticky, observer))
        }

        /**
         * 重寫LiveData的observer胸囱,把傳入的observer包裝一下
         */
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            observerSticky(owner,false, observer)
        }
    }

    class StickyObserver<T>(
        private val stickyLiveData: StickyLiveData<T>,
        private val sticky: Boolean,
        private val observer: Observer<in T>
    ) : Observer<T> {

        /**
         * 打個(gè)比方:
         * 一條數(shù)據(jù)祷舀,名稱為TestName,
         *      對(duì)應(yīng)一個(gè) StickyLiveData, 也就對(duì)應(yīng)一個(gè)version, 初始的值為0,且這個(gè)可以復(fù)用
         *      且會(huì)創(chuàng)建StickyObserver裳扯,對(duì)應(yīng)一個(gè) mLastVersion, 初始的值為0
         *
         * 如果 StickyLiveData#version 和 StickyObserver#mLastVersion 沒有對(duì)齊
         *      LastVersion < version --> 直接發(fā)送數(shù)據(jù)抛丽,就會(huì)產(chǎn)生黏性事件
         *
         * 源碼就是這樣沒對(duì)齊,所以無(wú)法控制黏性事件
         *
         * 因?yàn)樵创a的流程
         *      將傳入的observer包裝成LifecycleBoundObserver(繼承ObserverWrapper)會(huì)將傳入的observer做保存和保存在hashMap
         *      最后在considerNotify遍歷hashMap,活躍的觀察者會(huì)調(diào)用observer.onChanged(t)去發(fā)送數(shù)據(jù)
         *
         * 所以這里把傳入的observer包裝成StickyObserver 進(jìn)入源碼后 --> 再變成LifecycleBoundObserver
         * 所以最終發(fā)送數(shù)據(jù)會(huì)調(diào)用StickyObserver的onChanged 就可以做黏性事件的處理了
         *
         */
        private var mLastVersion = stickyLiveData.mLiveDataVersion

        override fun onChanged(t: T) {

            if (mLastVersion >= stickyLiveData.mLiveDataVersion) {
                if (sticky && stickyLiveData.mStickyData != null) {
                    observer.onChanged(stickyLiveData.mStickyData)
                }
                return
            }
            observer.onChanged(t)
        }
    }


}


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饰豺,一起剝皮案震驚了整個(gè)濱河市铺纽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哟忍,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陷寝,死亡現(xiàn)場(chǎng)離奇詭異锅很,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)凤跑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門爆安,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人仔引,你說(shuō)我怎么就攤上這事扔仓。” “怎么了咖耘?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵翘簇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我儿倒,道長(zhǎng)版保,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任夫否,我火速辦了婚禮彻犁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凰慈。我一直安慰自己汞幢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布微谓。 她就那樣靜靜地躺著森篷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪豺型。 梳的紋絲不亂的頭發(fā)上疾宏,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音触创,去河邊找鬼坎藐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岩馍。 我是一名探鬼主播碉咆,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蛀恩!你這毒婦竟也來(lái)了疫铜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤双谆,失蹤者是張志新(化名)和其女友劉穎壳咕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽馋,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谓厘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寸谜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竟稳。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖熊痴,靈堂內(nèi)的尸體忽然破棺而出他爸,到底是詐尸還是另有隱情,我是刑警寧澤果善,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布诊笤,位于F島的核電站,受9級(jí)特大地震影響巾陕,放射性物質(zhì)發(fā)生泄漏盏混。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一惜论、第九天 我趴在偏房一處隱蔽的房頂上張望许赃。 院中可真熱鬧,春花似錦馆类、人聲如沸混聊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)句喜。三九已至,卻和暖如春沟于,著一層夾襖步出監(jiān)牢的瞬間咳胃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工旷太, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留展懈,地道東北人销睁。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像存崖,于是被迫代替她去往敵國(guó)和親冻记。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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