RxJava 系列 (三)RxJava lift原理

前言

理解lift原理有什么意義泛范?

  • 可以理解Rxjava最核心的原理让虐,看懂了lift你就看懂了Rxjava

lift是Rxjava操作符的基礎(chǔ)原理,操作符是Rxjava功能如此豐富和好用的核心罢荡,理解了lift也就理解了Rxjava最核心的原理

  • 可以理解線程切換的原理赡突,有助于靈活運用線程切換和調(diào)試線程相關(guān)的問題

線程切換也是用的操作符,所以原理也是lift

RxJava基本用法

  1. 創(chuàng)建 Observer
Observer<String> observer = new Observer<String>() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }

    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }

    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};

Observer 需要實現(xiàn)三個方法区赵,相當(dāng)于定義了三種類型的事件

  • Subscriber 與 Observer的關(guān)系

Subscriber相當(dāng)于增加了Subscription(訂閱關(guān)系管理)功能的Observer
Subscription 包含兩個方法:
unsubscribe();(取消訂閱)
isUnsubscribed();(查詢訂閱關(guān)系)

Subscriber 還增加了一個onStart()方法:它會在 subscribe 剛開始惭缰,而事件還未發(fā)送之前被調(diào)用,可以用于做一些準備工作

實質(zhì)上笼才,在 RxJava 的 subscribe 過程中漱受,Observer 也總是會先被轉(zhuǎn)換成一個 Subscriber 再使用,所以為了統(tǒng)一患整,我們就統(tǒng)一以Subscriber來作為觀察者拜效,就不再提Observer了喷众。

  1. 創(chuàng)建 Observable
Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("Hello");
        subscriber.onNext("Hi");
        subscriber.onNext("Aloha");
        subscriber.onCompleted();
    }
});

創(chuàng)建Observable對象時,會傳入一個 OnSubscribe 對象紧憾,OnSubscribe對象會被存儲在生成的 Observable 對象中到千。

OnSubscribe 對象的作用,就是拿到 subscriber對象赴穗,向subscriber 對象發(fā)送事件憔四。
這里拿到觀察者Subscriber對象,并調(diào)用Subscriber實現(xiàn)的三個方法般眉,就是在向觀察者發(fā)送事件

  1. Subscribe (訂閱)
observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);

Observable.subscribe(Subscriber) 的內(nèi)部實現(xiàn)是這樣的(僅核心代碼):

// 注意:這不是 subscribe() 的源碼了赵,而是將源碼中與性能、兼容性甸赃、擴展性有關(guān)的代碼剔除后的核心代碼柿汛。
// 如果需要看源碼,可以去 RxJava 的 GitHub 倉庫下載埠对。
public Subscription subscribe(Subscriber subscriber) {
    subscriber.onStart();
    onSubscribe.call(subscriber);
    return subscriber;
}

可以看到络断,subscriber() 做了3件事:

  1. 調(diào)用 Subscriber.onStart() 。這個方法在前面已經(jīng)介紹過项玛,是一個可選的準備方法貌笨。
  2. 調(diào)用 Observable 中的 OnSubscribe.call(Subscriber) 。在這里襟沮,事件發(fā)送的邏輯開始運行锥惋。從這也可以看出,在 RxJava 中开伏, Observable 并不是在創(chuàng)建的時候就立即開始發(fā)送事件膀跌,而是在它被訂閱的時候,即當(dāng) subscribe() 方法執(zhí)行的時候硅则。
  3. 將傳入的 Subscriber 作為 Subscription 返回淹父。這是為了方便進行訂閱關(guān)系管理,比如 unsubscribe().

操作符-對事件序列進行變換

所謂變換怎虫,就是將事件序列中的對象或整個序列進行加工處理暑认,轉(zhuǎn)換成不同的事件或事件序列。概念說著總是模糊難懂的大审,來看 例子蘸际。

舉個例子:map()

Observable.just("images/logo.png") // 輸入類型 String
    .map(new Func1<String, Bitmap>() {
        @Override
        public Bitmap call(String filePath) { // 參數(shù)類型 String
            return getBitmapFromPath(filePath); // 返回類型 Bitmap
        }
    })
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) { // 參數(shù)類型 Bitmap
            showBitmap(bitmap);
        }
    });

map(): 對事件對象的直接變換。

變換的原理:lift()

這些變換雖然功能各有不同徒扶,但實質(zhì)上都是針對事件序列的處理和再發(fā)送粮彤。而在 RxJava 的內(nèi)部,它們是基于同一個基礎(chǔ)的變換方法: lift(Operator)。首先看一下 lift() 的內(nèi)部實現(xiàn)(僅核心代碼):

// 注意:這不是 lift() 的源碼导坟,而是將源碼中與性能屿良、兼容性、擴展性有關(guān)的代碼剔除后的核心代碼惫周。
// 如果需要看源碼尘惧,可以去 RxJava 的 GitHub 倉庫下載。
public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);
        }
    });
}

先不用關(guān)心細節(jié)递递,我們先知道 lift是接受一個operator參數(shù)喷橙,返回一個新的Observable對象。
在分析 lift() 的內(nèi)部實現(xiàn)之前登舞,我們先看一下加上操作符的一次調(diào)用的完整過程

一次包含操作符的調(diào)用的完整過程

Observable.just("images/logo.png") // 輸入類型 String
    .map(new Func1<String, Bitmap>() {
        @Override
        public Bitmap call(String filePath) { // 參數(shù)類型 String
            return getBitmapFromPath(filePath); // 返回類型 Bitmap
        }
    })
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) { // 參數(shù)類型 Bitmap
            showBitmap(bitmap);
        }
    });

做了三步:
1.生成observable對象
2.在observable對象上調(diào)用了map方法
3.在map方法的返回值上調(diào)用了.subscribe方法

問題:1.map方法內(nèi)部做了什么贰逾? 2.map方法的返回值是個什么?

回答兩個問題

因為map()內(nèi)部不是直接調(diào)用的lift方法(跟lift的原理一樣菠秒,只是沒有直接使用lift方法)疙剑,所以我們以take()操作符的源碼為例,來看方法調(diào)用稽煤。


回答兩個問題:

  1. take方法內(nèi)部很簡單核芽,就是調(diào)用了lift方法
    2.返回值就是lift方法的返回值,是個新new的observable對象酵熙。

將示例稍作修改

        Observable observable1 = Observable.just("images/logo.png"); // 輸入類型 String
        
        Observable observable2 = observable1.map(new Func1<String, Bitmap>() {
                    @Override
                    public Bitmap call(String filePath) { // 參數(shù)類型 String
                        return getBitmapFromPath(filePath); // 返回類型 Bitmap
                    }
                });
        
        observable2.subscribe(new Action1<Bitmap>() {
                    @Override
                    public void call(Bitmap bitmap) { // 參數(shù)類型 Bitmap
                        showBitmap(bitmap);
                    }
                });
observable2.subscribe 分析

回顧前面subscribe()的內(nèi)部實現(xiàn),我們發(fā)現(xiàn)observable2里的OnSubscribe對象的call方法會被調(diào)用驰坊。而observable2就是lift方法返回的Observable對象匾二, observable2里的onSubscribe對象就是lift的核心重點。

observable2里的OnSubscribe對象 —lift的核心重點

observable2對象里面通過observable1對象的onSubscribe.call(newSubscriber)達到通知observable1目的拳芙。
因為observable1對象是我們初始的Observable對象察藐,它的onSubscribe.call會發(fā)送事件到newSubscriber。
newSubscriber是operator.call(subscriber)返回的舟扎,newSubscriber做了兩件事:
1.進行事件的變換操作分飞。newSubscriber能拿到初始的事件,可以進行轉(zhuǎn)換操作睹限,這也是操作符發(fā)生效力的地方譬猫,不同的操作符的作用就是對事件進行不同的轉(zhuǎn)換。
2.轉(zhuǎn)發(fā)給subscriber羡疗,newSubscriber有subscriber的引用染服,可以將轉(zhuǎn)換后的事件轉(zhuǎn)發(fā)給subscriber,也就是最終的訂閱者叨恨。
lift方法返回的observable2對象在調(diào)用鏈的中間起到了一個中轉(zhuǎn)的作用柳刮,這就是lift原理的核心。

operator.call內(nèi)部通過傳入一個subscriber返回一個newSubscriber,newSubscriber能達到事件轉(zhuǎn)換和轉(zhuǎn)發(fā)的目的秉颗。
是如何做到的痢毒?
我們來舉個例子

Operator的一個例子

多個操作符的情況

多個操作符相當(dāng)于中間經(jīng)過了多層中轉(zhuǎn),原理都一樣

關(guān)于事件發(fā)送的觸發(fā)

結(jié)合上面多個操作符的圖蚕甥,強調(diào)一下:

事件發(fā)送的觸發(fā)是從調(diào)用subscribe()方法后開始的闸准,前面哪怕調(diào)用了N多的操作符方法只要還沒有調(diào)用subscribe()方法其實并沒有觸發(fā)事件的發(fā)送。

事件發(fā)送之前有一個從下往上通知的過程梢灭,當(dāng)subscribe()方法被調(diào)用之后夷家,先是通過observable2對象里調(diào)用observable1對象的onSubscribe.call通知observable1對象,如果observable1對象不是最開始發(fā)送事件的Observable對象(多個操作符的情況)敏释,那么同樣的還會繼續(xù)往上通知库快,直到通知到初始的Observable對象,才會開始事件的發(fā)送钥顽。

所以RxJava是 先從下往上通知义屏,然后再從上往下發(fā)送事件

參考文獻

給 Android 開發(fā)者的 RxJava 詳解 --扔物線
http://gank.io/post/560e15be2dca930e00da1083

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜂大,隨后出現(xiàn)的幾起案子闽铐,更是在濱河造成了極大的恐慌,老刑警劉巖奶浦,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兄墅,死亡現(xiàn)場離奇詭異,居然都是意外死亡澳叉,警方通過查閱死者的電腦和手機隙咸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來成洗,“玉大人五督,你說我怎么就攤上這事∑垦辏” “怎么了充包?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長遥椿。 經(jīng)常有香客問我基矮,道長,這世上最難降的妖魔是什么修壕? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任愈捅,我火速辦了婚禮,結(jié)果婚禮上慈鸠,老公的妹妹穿的比我還像新娘蓝谨。我一直安慰自己灌具,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布譬巫。 她就那樣靜靜地躺著咖楣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芦昔。 梳的紋絲不亂的頭發(fā)上诱贿,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音咕缎,去河邊找鬼珠十。 笑死,一個胖子當(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
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年姿搜,在試婚紗的時候發(fā)現(xiàn)自己被綠了寡润。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡舅柜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出躲惰,到底是詐尸還是另有隱情致份,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布础拨,位于F島的核電站氮块,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诡宗。R本人自食惡果不足惜滔蝉,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塔沃。 院中可真熱鬧蝠引,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吊洼,卻和暖如春训貌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冒窍。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工递沪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人综液。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓款慨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親意乓。 傳聞我的和親對象是個殘疾皇子樱调,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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

  • 我從去年開始使用 RxJava ,到現(xiàn)在一年多了届良。今年加入了 Flipboard 后笆凌,看到 Flipboard 的...
    Jason_andy閱讀 5,460評論 7 62
  • 前言我從去年開始使用 RxJava ,到現(xiàn)在一年多了士葫。今年加入了 Flipboard 后乞而,看到 Flipboard...
    占導(dǎo)zqq閱讀 9,159評論 6 151
  • 最近項目里面有用到Rxjava框架爪模,感覺很強大的巨作,所以在網(wǎng)上搜了很多相關(guān)文章荚藻,發(fā)現(xiàn)一片文章很不錯屋灌,今天把這篇文...
    Scus閱讀 6,866評論 2 50
  • Github:https://github.com/ReactiveX/RxJavahttps://github....
    才兄說閱讀 1,631評論 2 10
  • 心中的暖意是從上次見你一面的時候開始的共郭,因此我許久都不能為之確定心里仿佛出現(xiàn)了一個大洞的心情是由于你而造成的,還是...
    程程有趣閱讀 890評論 7 12