RxJava lift()原理

lift()方法是RxJava中所有操作符的基礎(chǔ)鹃骂,可以通過(guò)它做各種各樣的變化。弄清楚它的原理,也方便我們理解其他操作符园骆。首先先看幾個(gè)相關(guān)接口。

Func1 接口

public interface Func1<T, R> extends Function {
    R call(T t);
}

Func1接口會(huì)按照泛型參數(shù)的順序傳入T寓调,并返回R锌唾。

Operator 接口

public interface Operator<R, T> extends Func1<Subscriber<? super R>, Subscriber<? super T>>

按照Func1接口的定義,Operator接口會(huì)傳入一個(gè)Subscriber<? super R>參數(shù)夺英,并返回一個(gè)Subscriber<? super T>晌涕。

關(guān)于Operator和lift()中泛型順序的問(wèn)題

也許有人(is me)第一眼看到Observable<T>Operator<R, T>痛悯,Func1<T, R>這幾個(gè)類(lèi)的泛型參數(shù)余黎,頭都大了,關(guān)鍵是Operator的泛型參數(shù)順序?yàn)槭裁词?code>R, T载萌,而不是T, R惧财?

其實(shí)這里不需要關(guān)心順序是什么,只需要記住Operator<R, T>是按照泛型參數(shù)的順序炒考,傳入一個(gè)Subscriber<R>參數(shù)可缚,并返回一個(gè)Subscriber<T>,寫(xiě)成Operator<A, B>或者Operator<M, N>是沒(méi)有任何區(qū)別的斋枢。

lift()調(diào)用流程

首先需要記住lift()方法是在一個(gè)已有Observable上調(diào)用的帘靡。

lift()方法核心代碼:

public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
    return new Observable<R>(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber<? super R> o) {
            Subscriber<? super T> st = operator.call(o);
            st.onStart();
            // 這里的onSubscribe是調(diào)用lift方法的Observable中的onSubscribe
            onSubscribe.call(st);
        }
    });
}

根據(jù)代碼的調(diào)用流程來(lái)分析:

1、假設(shè)已有一個(gè)Observable<T>瓤帚,調(diào)用lift()方法描姚,生成一個(gè)Observable<R>,此時(shí)就有了兩個(gè)Observable和兩個(gè)OnSubscribe對(duì)象戈次。

2轩勘、然后調(diào)用Observable<R>subscribe()方法,傳入一個(gè)Subscriber<R>對(duì)象怯邪,此時(shí)觸發(fā)Observable<R>.onSubscribe.call()方法绊寻,也就是上面lift()方法中的call()方法。

3悬秉、在該方法中會(huì)調(diào)用onSubscribe.call()方法澄步,注意這個(gè)onSubscribeObservable<T>中的那個(gè)OnSubscribe<T>對(duì)象,它需要傳入一個(gè)Subscriber<T>對(duì)象和泌,這個(gè)對(duì)象是通過(guò)operator.call()方法生成的村缸。正是這個(gè)Operator對(duì)象將兩個(gè)Subscriber對(duì)象關(guān)聯(lián)起來(lái),OnSubscribe<T>在執(zhí)行Subscriber<T>.onNext(T t)方法的時(shí)候也會(huì)執(zhí)行Subscriber<R>.onNext(R r)武氓,而這里從T變成R梯皿,正好用到了傳到Operator中的參數(shù)Func1<T, R>仇箱。

4、如果具體化一點(diǎn)东羹,上面的Observable<T>就是事件源剂桥,對(duì)它進(jìn)行lift()變換得到新的Observable<R>,這個(gè)新的Observable的回調(diào)已經(jīng)固定百姓,相當(dāng)于是一個(gè)模板(也就是上面lift()方法中的call()方法)渊额。這時(shí)調(diào)用subscribe()况木,傳入的Subscriber<R>是用戶(hù)定義的事件監(jiān)聽(tīng)者垒拢,但它監(jiān)聽(tīng)的是新的Observable<R>,這個(gè)Observable的回調(diào)是固定的火惊,它并不能產(chǎn)生新事件求类,所以得靠事件源Observable<T>。這個(gè)時(shí)候Operator生成一個(gè)中間的Subscriber<T>對(duì)象屹耐,該對(duì)象的作用就是接收事件源的事件尸疆,并將事件轉(zhuǎn)給用戶(hù)定義的Subscriber。這個(gè)Subscriber<T>并沒(méi)有消耗事件惶岭,而是起著一個(gè)代理的作用寿弱。所以Operator可以看做是一個(gè)生成代理的工具類(lèi)。在這個(gè)轉(zhuǎn)發(fā)過(guò)程中有一個(gè)數(shù)據(jù)類(lèi)型的變化過(guò)程按灶,也是通過(guò)Operator轉(zhuǎn)換器Func1實(shí)現(xiàn)的症革,想怎樣轉(zhuǎn)換數(shù)據(jù),也是用戶(hù)定義后傳到Operator中的鸯旁。

小結(jié)

1噪矛、我們需要把Observable的調(diào)用看做一條

2铺罢、對(duì)于Observable<T> -> Observable<R>這個(gè)變化艇挨,訂閱者為Subscriber<R>,在subscriber()方法調(diào)用后韭赘,流的順序?yàn)?strong>倒序的,即從Observable<R> -> Observable<T>,因?yàn)槲覀兪冀K需要調(diào)用最開(kāi)始的事件源。為了滿(mǎn)足這個(gè)需求,會(huì)通過(guò)Operator<R, T>這個(gè)代理工具生成一個(gè)代理Subscriber<T>,這也解釋了為什么在聲明Operator時(shí)泛型參數(shù)的順序?qū)憺?code>R, T,正好可以和這一變化對(duì)應(yīng)起來(lái)铺峭,用相同的泛型參數(shù)更便于理解虱朵。這樣準(zhǔn)備工作就都做好了。

3、Observable<T>開(kāi)始向Subscriber<T>發(fā)送事件,發(fā)送的參數(shù)類(lèi)型為T,這時(shí)候通過(guò)轉(zhuǎn)換器Func1T變成R铣揉,這樣就能順利的通過(guò)代理Subscriber<T>將事件發(fā)送給Subscriber<R>了朽合。

4休讳、所以流的路線(xiàn)為Observable<R> -> Observable<T> -> Subscriber<T> -> Subscriber<R>俊柔。一條線(xiàn)分成兩部分,前半部分為準(zhǔn)備工作活合,后半部分為執(zhí)行操作雏婶。

下圖是lift()的過(guò)程,其中虛線(xiàn)箭頭代表生成芜辕,實(shí)線(xiàn)箭頭代表調(diào)用尚骄。也可以參考 扔物線(xiàn) - 給 Android 開(kāi)發(fā)者的 RxJava 詳解 中的配圖。

Image.png

map()方法

map()方法是RxJava中使用lift()最簡(jiǎn)單的方法侵续,如果上面lift()方法過(guò)于抽象,可以通過(guò)該方法來(lái)加深理解憨闰。

public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
    return lift(new OperatorMap<T, R>(func));
}
public final class OperatorMap<T, R> implements Operator<R, T> {

    private final Func1<? super T, ? extends R> transformer;

    public OperatorMap(Func1<? super T, ? extends R> transformer) {
        this.transformer = transformer;
    }

    @Override
    public Subscriber<? super T> call(final Subscriber<? super R> o) {
        return new Subscriber<T>(o) {

            @Override
            public void onCompleted() {
                o.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                o.onError(e);
            }

            @Override
            public void onNext(T t) {
                try {
                    o.onNext(transformer.call(t));
                } catch (Throwable e) {
                    Exceptions.throwOrReport(e, this, t);
                }
            }

        };
    }

}

看到OperatorMap.call()方法状蜗,它直接生成一個(gè)新的Subscriber,通過(guò)上面的分析可以知道鹉动,這是一個(gè)代理Subscriber轧坎,所以它的onNext()等方法都只是直接調(diào)用了外部傳進(jìn)來(lái)的Subscriber

舉個(gè)例子:

Observable.just(1.34f, 8.3453f, -534.34f, 392.99f)
        .map(new Func1<Float, Integer>() {
            @Override
            public Integer call(Float aFloat) {
                return Math.round(aFloat);
            }
        })
        .map(new Func1<Integer, String>() {
            @Override
            public String call(Integer integer) {
                return Integer.toBinaryString(integer);
            }
        })
        .subscribe(new Action1<String>() {
            @Override
            public void call(String s) {
                log("2 map onNext->" + s);
            }
        });

// outputs
// 2 map onNext->1
// 2 map onNext->1000
// 2 map onNext->11111111111111111111110111101010
// 2 map onNext->110001001

該例子是一個(gè)Float->Integer->String的轉(zhuǎn)換泽示。我們按上面的流程來(lái)分析缸血。

1、生成一個(gè)Observable<Float>械筛。

2捎泻、調(diào)用map()生成一個(gè)Observable<Integer>

3埋哟、再調(diào)用map()生成一個(gè)Observable<String>笆豁。

4、subscribe()一個(gè)Subscriber<String>赤赊。至此流的前半部分完成闯狱。

5、執(zhí)行開(kāi)始抛计,Observable<String>發(fā)送事件哄孤,先生成一個(gè)Subscriber<Integer>傳給Observable<Integer>Observable<Integer>.onSubscribe.call())。

6吹截、Observable<Integer>開(kāi)始發(fā)送事件瘦陈,同樣的生成一個(gè)Subscriber<Float>傳給Observable<Float>Observable<Float>.onSubscribe.call())朦肘。

7、真正發(fā)送事件開(kāi)始双饥,Observable<Float>調(diào)用Subscriber<Float>.onNext(Float)等方法媒抠,同時(shí)Subscriber<Integer>.onNext(Integer)被調(diào)用,同時(shí)Subscriber<String>.onNext(String)被調(diào)用咏花,事件發(fā)送完成趴生。

8、雖然是流的模型昏翰,但其實(shí)是一堆內(nèi)部類(lèi)和外部類(lèi)的嵌套關(guān)系苍匆。

參考資料

給 Android 開(kāi)發(fā)者的 RxJava 詳解 - 變換的原理:lift()

謎之RxJava (二) —— Magic Lift

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市棚菊,隨后出現(xiàn)的幾起案子浸踩,更是在濱河造成了極大的恐慌,老刑警劉巖统求,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件检碗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡码邻,警方通過(guò)查閱死者的電腦和手機(jī)折剃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)像屋,“玉大人怕犁,你說(shuō)我怎么就攤上這事〖狠海” “怎么了奏甫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵节芥,是天一觀的道長(zhǎng)暂论。 經(jīng)常有香客問(wèn)我,道長(zhǎng)瓦糕,這世上最難降的妖魔是什么胁艰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任款筑,我火速辦了婚禮,結(jié)果婚禮上腾么,老公的妹妹穿的比我還像新娘奈梳。我一直安慰自己,他們只是感情好解虱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布攘须。 她就那樣靜靜地躺著,像睡著了一般殴泰。 火紅的嫁衣襯著肌膚如雪于宙。 梳的紋絲不亂的頭發(fā)上浮驳,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音捞魁,去河邊找鬼至会。 笑死,一個(gè)胖子當(dāng)著我的面吹牛谱俭,可吹牛的內(nèi)容都是我干的奉件。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼昆著,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼县貌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起凑懂,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤煤痕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后接谨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體摆碉,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年疤坝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兆解。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡跑揉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出埠巨,到底是詐尸還是另有隱情历谍,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布辣垒,位于F島的核電站望侈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏勋桶。R本人自食惡果不足惜脱衙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望例驹。 院中可真熱鬧捐韩,春花似錦、人聲如沸鹃锈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)屎债。三九已至仅政,卻和暖如春垢油,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背圆丹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工滩愁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辫封。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓硝枉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親秸讹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子檀咙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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