RxJava藏杖、RxAndroid

https://gank.io/post/560e15be2dca930e00da1083

觀察者模式

RxJava 的異步實現(xiàn),是通過一種擴展的觀察者模式來實現(xiàn)的脉顿。
A 對象(觀察者)對 B 對象(被觀察者)的某種變化高度敏感蝌麸,需要在 B 變化的一瞬間做出反應(yīng)。采用注冊(Register)或者稱為訂閱(Subscribe)的方式艾疟,告訴被觀察者:我需要你的某某狀態(tài)来吩,你要在它變化的時候通知我敢辩。 Android 開發(fā)中一個比較典型的例子是點擊監(jiān)聽器 OnClickListener 谍倦。對設(shè)置 OnClickListener 來說麦射, View 是被觀察者, OnClickListener 是觀察者泣港,二者通過 setOnClickListener() 方法達成訂閱關(guān)系兽间。訂閱之后用戶點擊按鈕的瞬間历葛,Android Framework 就會將點擊事件發(fā)送給已經(jīng)注冊的 OnClickListener 。采取這樣被動的觀察方式嘀略,既省去了反復(fù)檢索狀態(tài)的資源消耗恤溶,也能夠得到最高的反饋速度。通過 setOnClickListener() 方法帜羊,Button 持有 OnClickListener 的引用(這一過程沒有在圖上畫出)咒程;當用戶點擊時,Button 自動調(diào)用 OnClickListener 的 onClick() 方法讼育。

一帐姻、RxJava 的觀察者模式

RxJava 有四個基本概念:Observable (被觀察者)、 Observer (觀察者)奶段、 subscribe (訂閱)饥瓷、事件。Observable 和 Observer 通過 subscribe() 方法實現(xiàn)訂閱關(guān)系痹籍,從而 Observable 可以在需要的時候發(fā)出事件來通知 Observer呢铆。

與傳統(tǒng)觀察者模式不同, RxJava 的事件回調(diào)方法除了普通事件 onNext() (相當于 onClick() / onEvent())之外蹲缠,還定義了兩個特殊的事件:onCompleted() 和 onError()棺克。
?onCompleted(): 事件隊列完結(jié)。RxJava 不僅把每個事件單獨處理线定,還會把它們看做一個隊列娜谊。RxJava 規(guī)定,當不會再有新的 onNext() 發(fā)出時斤讥,需要觸發(fā) onCompleted() 方法作為標志纱皆。
?onError(): 事件隊列異常。在事件處理過程中出異常時周偎,onError() 會被觸發(fā)抹剩,同時隊列自動終止,不允許再有事件發(fā)出蓉坎。
?在一個正確運行的事件序列中, onCompleted() 和 onError() 有且只有一個,并且是事件序列中的最后一個胡嘿。需要注意的是蛉艾,onCompleted() 和 onError() 二者也是互斥的,即在隊列中調(diào)用了其中一個,就不應(yīng)該再調(diào)用另一個勿侯。

二拓瞪、基本實現(xiàn)

compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex:rxandroid:1.2.0'

  1. 創(chuàng)建 Observer
Observer<String> observer = new Observer<String>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(String s) {

            }
        };

除了 Observer 接口之外,RxJava 還內(nèi)置了一個實現(xiàn)了 Observer 的抽象類:Subscriber助琐。

Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(String s) {

            }
        };

實質(zhì)上祭埂,在 RxJava 的 subscribe 過程中,Observer 也總是會先被轉(zhuǎn)換成一個 Subscriber 再使用兵钮。它們的區(qū)別對于使用者來說主要有兩點:
①onStart(): 這是 Subscriber 增加的方法蛆橡。它會在 subscribe 剛開始,而事件還未發(fā)送之前被調(diào)用掘譬,可以用于做一些準備工作泰演,例如數(shù)據(jù)的清零或重置。這是一個可選方法葱轩,默認情況下它的實現(xiàn)為空睦焕。需要注意的是,如果對準備工作的線程有要求(例如彈出一個顯示進度的對話框靴拱,這必須在主線程執(zhí)行)垃喊, onStart() 就不適用了,因為它總是在 subscribe 所發(fā)生的線程被調(diào)用袜炕,而不能指定線程缔御。要在指定的線程來做準備工作,可以使用 doOnSubscribe() 方法妇蛀,具體可以在后面的文中看到耕突。
②unsubscribe(): 這是 Subscriber 所實現(xiàn)的另一個接口 Subscription 的方法,用于取消訂閱评架。在這個方法被調(diào)用后眷茁,Subscriber 將不再接收事件。一般在這個方法調(diào)用前纵诞,可以使用 isUnsubscribed() 先判斷一下狀態(tài)上祈。 unsubscribe() 這個方法很重要,因為在 subscribe() 之后浙芙, Observable 會持有 Subscriber 的引用登刺,這個引用如果不能及時被釋放,將有內(nèi)存泄露的風險嗡呼。所以最好保持一個原則:要在不再使用的時候盡快在合適的地方(例如 onPause() onStop() 等方法中)調(diào)用 unsubscribe() 來解除引用關(guān)系纸俭,以避免內(nèi)存泄露的發(fā)生。

  1. 創(chuàng)建 Observable
    RxJava 使用 create() 方法來創(chuàng)建一個 Observable 南窗,并為它定義事件觸發(fā)規(guī)則:
Observable<String> 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();
            }
        });

這里傳入了一個 OnSubscribe 對象作為參數(shù)揍很。OnSubscribe 會被存儲在返回的 Observable 對象中郎楼,它的作用相當于一個計劃表,當 Observable 被訂閱的時候窒悔,OnSubscribe 的 call() 方法會自動被調(diào)用呜袁,事件序列就會依照設(shè)定依次觸發(fā)(對于上面的代碼,就是觀察者Subscriber 將會被調(diào)用三次 onNext() 和一次 onCompleted())简珠。這樣阶界,由被觀察者調(diào)用了觀察者的回調(diào)方法,就實現(xiàn)了由被觀察者向觀察者的事件傳遞聋庵,即觀察者模式膘融。
just(T...): 將傳入的參數(shù)依次發(fā)送出來。

Observable observable = Observable.just("Hello", "Hi", "Aloha");
// 將會依次調(diào)用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();

from(T[]) / from(Iterable<? extends T>) : 將傳入的數(shù)組或 Iterable 拆分成具體對象后珍策,依次發(fā)送出來托启。

String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
// 將會依次調(diào)用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
  1. Subscribe (訂閱)
observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);

可以看到,subscriber() 做了3件事:
1.調(diào)用 Subscriber.onStart() 攘宙。這個方法在前面已經(jīng)介紹過屯耸,是一個可選的準備方法。
2.調(diào)用 Observable 中的 OnSubscribe.call(Subscriber) 蹭劈。在這里疗绣,事件發(fā)送的邏輯開始運行。從這也可以看出铺韧,在 RxJava 中多矮, Observable 并不是在創(chuàng)建的時候就立即開始發(fā)送事件,而是在它被訂閱的時候哈打,即當 subscribe() 方法執(zhí)行的時候塔逃。
3.將傳入的 Subscriber 作為 Subscription 返回。這是為了方便 unsubscribe().
除了 subscribe(Observer) 和 subscribe(Subscriber) 料仗,subscribe() 還支持不完整定義的回調(diào)湾盗,RxJava 會自動根據(jù)定義創(chuàng)建出 Subscriber 。形式如下:

Action1<String> onNextAction = new Action1<String>() {
    // onNext()
    @Override
    public void call(String s) {
        Log.d(tag, s);
    }
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
    // onError()
    @Override
    public void call(Throwable throwable) {
        // Error handling
    }
};
Action0 onCompletedAction = new Action0() {
    // onCompleted()
    @Override
    public void call() {
        Log.d(tag, "completed");
    }
};

// 自動創(chuàng)建 Subscriber 立轧,并使用 onNextAction 來定義 onNext()
observable.subscribe(onNextAction);
// 自動創(chuàng)建 Subscriber 格粪,并使用 onNextAction 和 onErrorAction 來定義 onNext() 和 onError()
observable.subscribe(onNextAction, onErrorAction);
// 自動創(chuàng)建 Subscriber ,并使用 onNextAction氛改、 onErrorAction 和 onCompletedAction 來定義 onNext()帐萎、 onError() 和 onCompleted()
observable.subscribe(onNextAction, onErrorAction, onCompletedAction);

RxJava 是提供了多個 ActionX 形式的接口 (例如 Action2, Action3) 的,它們可以被用以包裝不同的無返回值的方法胜卤。

三疆导、線程控制 —— Scheduler

觀察者模式本身的目的就是『后臺處理,前臺回調(diào)』的異步機制瑰艘。在不指定線程的情況下是鬼, RxJava 遵循的是線程不變的原則肤舞。
?Schedulers.immediate(): 直接在當前線程運行紫新,相當于不指定線程均蜜。這是默認的 Scheduler。
?Schedulers.newThread(): 總是啟用新線程芒率,并在新線程執(zhí)行操作囤耳。
?Schedulers.io(): I/O 操作(讀寫文件、讀寫數(shù)據(jù)庫偶芍、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler充择。行為模式和 newThread() 差不多,區(qū)別在于 io() 的內(nèi)部實現(xiàn)是是用一個無數(shù)量上限的線程池匪蟀,可以重用空閑的線程椎麦,因此多數(shù)情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中材彪,可以避免創(chuàng)建不必要的線程观挎。
?Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算段化,即不會被 I/O 等操作限制性能的操作嘁捷,例如圖形的計算。這個 Scheduler 使用的固定的線程池显熏,大小為 CPU 核數(shù)雄嚣。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU喘蟆。
?另外缓升, Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行蕴轨。
subscribeOn(): 指定 subscribe() 所發(fā)生的線程港谊,即 Observable.OnSubscribe 被激活時所處的線程〕咂澹或者叫做事件產(chǎn)生的線程封锉。 * observeOn(): 指定 Subscriber 所運行在的線程”烀或者叫做事件消費的線程成福。

Observable.just(1, 2, 3, 4)
    .subscribeOn(Schedulers.io()) // 指定 subscribe() 發(fā)生在 IO 線程
    .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調(diào)發(fā)生在主線程
    .subscribe(new Action1<Integer>() {
        @Override
        public void call(Integer number) {
            Log.d(tag, "number:" + number);
        }
    });

由于 subscribeOn(Schedulers.io()) 的指定,被創(chuàng)建的事件的內(nèi)容 1荆残、2奴艾、3、4 將會在 IO 線程發(fā)出内斯;而由于 observeOn(AndroidScheculers.mainThread()) 的指定蕴潦,因此 subscriber 數(shù)字的打印將發(fā)生在主線程 像啼。事實上,這種在 subscribe() 之前寫上兩句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式非常常見潭苞,它適用于多數(shù)的 『后臺線程取數(shù)據(jù)忽冻,主線程顯示』的程序策略。

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() 方法將參數(shù)中的 String 對象轉(zhuǎn)換成一個 Bitmap 對象后返回此疹,而在經(jīng)過 map() 方法后僧诚,事件的參數(shù)類型也由 String 轉(zhuǎn)為了 Bitmap。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝗碎,一起剝皮案震驚了整個濱河市湖笨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹦骑,老刑警劉巖慈省,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異眠菇,居然都是意外死亡边败,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門琼锋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來放闺,“玉大人,你說我怎么就攤上這事缕坎〔勒欤” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵谜叹,是天一觀的道長匾寝。 經(jīng)常有香客問我,道長荷腊,這世上最難降的妖魔是什么艳悔? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮女仰,結(jié)果婚禮上猜年,老公的妹妹穿的比我還像新娘。我一直安慰自己疾忍,他們只是感情好乔外,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著一罩,像睡著了一般杨幼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天差购,我揣著相機與錄音四瘫,去河邊找鬼。 笑死欲逃,一個胖子當著我的面吹牛找蜜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暖夭,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼锹杈,長吁一口氣:“原來是場噩夢啊……” “哼撵孤!你這毒婦竟也來了迈着?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤邪码,失蹤者是張志新(化名)和其女友劉穎裕菠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闭专,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡奴潘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了影钉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片画髓。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖平委,靈堂內(nèi)的尸體忽然破棺而出奈虾,到底是詐尸還是另有隱情,我是刑警寧澤廉赔,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布肉微,位于F島的核電站,受9級特大地震影響蜡塌,放射性物質(zhì)發(fā)生泄漏碉纳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一馏艾、第九天 我趴在偏房一處隱蔽的房頂上張望劳曹。 院中可真熱鬧,春花似錦琅摩、人聲如沸铁孵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽库菲。三九已至,卻和暖如春志膀,著一層夾襖步出監(jiān)牢的瞬間熙宇,已是汗流浹背鳖擒。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烫止,地道東北人蒋荚。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像馆蠕,于是被迫代替她去往敵國和親期升。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

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