RxJava學習 - 基本使用

RxJava的觀察者模式

RxJava有四個基本概念:Observale(被觀察者)、Observer(觀察者)、subscribe(訂閱)、事件
Observable 和 Observer 通過 subscribe() 方法實現訂閱關系,從而 Observable 可以在需要的時候發出事件來通知 Observer。
與傳統觀察者模式不同,RxJava的事件回調方法除了普通事件onNext()之外,還定義了兩個特殊的事件:onCompleted()onError()

onCompleted(): 事件隊列完結。RxJava不僅把每個事件單獨處理,還會把它們看做一個隊列。RxJava規定,當不會再有新的onNext()發出時,需要觸發onCompleted()方法作為標志。
onError():事件隊列異常。在事件處理過程中出異常時,onError()會被觸發,同時隊列自動終止,不允許再有事件發出。
在一個正確運行的事件序列中,onCompleted()onError()有且只有一個,并且是事件序列中的最后一個。需要注意的是,onCompleted()onError()是互斥的,即在隊列中調用了其中一個,就不應該再調用另外一個。

基本使用

創建 Observer

Observer 即觀察者,它決定事件觸發的時候將有怎樣的行為。 RxJava 中的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接口之外,RxJava 還內置了一個實現了Observer的抽象類:SubscriberSubscriberObserver接口進行了一些擴展,但他們的基本使用方式是完全一樣的:

Subscriber<String> subscriber = new Subscriber<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!");
    }
};

不僅基本使用方式一樣,實質上,在RxJava的subscribe(訂閱)過程中,Observer也總是會先被轉換成一個Subscriber再使用。所以如果只想使用基本功能,選擇ObserverSubscriber是完全一樣的。它們的區別對于使用者來說主要有兩點:

onStart():這是Subscriber增加的方法。它會在subscribe(訂閱)剛開始,而事件還未發送之前被調用,可以用于做一些準備工作,例如數據的清零或重置。這是一個可選方法,默認情況下它的實現為空。需要注意的是,如果對準備工作的線程有要求(例如彈出一個顯示進度的對話框,這必須在主線程執行),onStart()就不適用了,因為它總是在subscribe(訂閱)所發生的線程被調用,而不能指定線程。要在指定的線程來做準備工作,可以使用doOnSubscribe()方法,具體可以在后面的文章中看到。
unsubscribe():這是Subscriber所實現的另外一個接口Subscription的方法,用于取消訂閱。在這個方法被調用后,Subscriber將不再接收事件。一般在這個方法調用前,可以使用isUnsubscribed()先判斷一下狀態。isUnsubscribed()這個方法很重要,因為在subscribe()之后,Observable會持有Subscriber的引用,這個引用如果不能及時被釋放,將有內存泄漏的風險。所以最好保持一個原則:要在不再使用的時候盡快在合適的地方(如onPause()onStop()等方法中)調用unsubscribe來解除易用關系,以避免內存泄漏的發生。

創建Observable

Observable就是被觀察者,它決定什么時候觸發事件以及觸發怎樣的事件。 RxJava 使用 create()
方法來創建一個 Observable ,并為它定義事件觸發規則:

//定義被觀察者,
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        if (!subscriber.isUnsubscribed()) {
            subscriber.onNext("hello");
            subscriber.onNext("hi");            
            subscriber.onNext("world");
            subscriber.onCompleted();
        }
    }
});

我們可以看到,Observable的創建是使用create(),并且傳入了一個OnSubscribe對象作為參數。OnSubscribe會被存儲在返回的Observable對象中,它的作用相當于一個計劃表,當Observable被訂閱的時候,OnSubscribecall方法會自動被調用,事件序列就會依照設定依次觸發。

這個例子很簡單:事件的內容是字符串,而不是一些復雜的對象;事件的內容是已經定好了的,而不像有的觀察者模式一樣是待確定的(例如網絡請求的結果在請求返回之前是未知的);所有事件在一瞬間被全部發送出去,而不是夾雜一些確定或不確定的時間間隔或者經過某種觸發器來觸發的。總之,這個例子看起來毫無實用價值。但這是為了便于說明,實質上只要你想,各種各樣的事件發送規則你都可以自己來寫。至于具體怎么做,后面都會講到,但現在不行。只有把基礎原理先說明白了,上層的運用才能更容易說清楚。

create()方法是RxJava最基本的創造事件序列的方法。基于這個方法,RxJava還提供了一些方法用來快捷創建事件隊列,例如:

  • just(T...):將傳入的參數依次發送出來
String[] item1 = {"hello","world"};
String[] item2 = {"RxJava", "just"};
Observable observable = Observable.just(item1, item2);
//將會依次調用:
//onNext("hello");
//onNext("world");
//onNext("RxJava");
//onNext("just");
// onCompleted();
  • from(T[])/from(Iterable<? extends T>):將傳入的數組或者Iterable拆分成具體對象后,依次發送出來
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
// 將會依次調用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();

Subscribe(訂閱)

創建了ObservableObserver之后,再用subscribe()方法將它們聯結起來,就可以工作了:

observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);

Observable.subscribe(Subscriber)內部實現是這樣的:

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

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

1.調用subscriber.onStart();
2.調用Observable中的OnSubscribe.call(subscriber)。在這里,事件發送的邏輯開始運行。從這也可以看出,在RxJava中,Observable并不是在創建的時間就立即開始發送事件,而是在它被訂閱的時候,即當subscribe()方法執行的時候。
3.將傳入的subscriber返回。這是為了方便unsubscribe();

整體流程

整體流程

不完整定義的回調

除了subscribe(Observer)subscribe(Subscriber),subscribe()還支持不完整定義的回調,RxJava會自動根據定義創建出Subscribeer

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");
    }
};

// 自動創建 Subscriber ,并使用 onNextAction 來定義 onNext()
observable.subscribe(onNextAction);
// 自動創建 Subscriber ,并使用 onNextAction 和 onErrorAction 來定義 onNext() 和 onError()
observable.subscribe(onNextAction, onErrorAction);
// 自動創建 Subscriber ,并使用 onNextAction、 onErrorAction 和 onCompletedAction 來定義 onNext()、 onError() 和 onCompleted()
observable.subscribe(onNextAction, onErrorAction, onCompletedAction);

簡單解釋一下這段代碼中出現的 Action1 和 Action0。
Action0:Action0是RxJava中的一個接口,它只有一個call()方法,這個方法是無參無返回值的;由于Subscriber中的onCompleted()方法也是無參無返回值的,因此Action0可以被當作一個包裝對象,將onCompleted()的內容打包起來將自己作為一個參數傳入subscribe()以實現不完整定義的回調。這樣也就相當于把onCompleted()方法作為參數傳進了subscribe()
Action1:Action1也是RxJava中的一個接口,它同樣也只有call(T param)一個方法,這個方法也無返回值,但有一個參數;與Action0同理,由于onNext(T obj)onError(Throwable e)也是單參數無返回值的,因為Action1可以將onNext(T obj)onError(Throwable e)打包起來傳入subscribe()以實現不完整定義的回調。事實上,雖然 Action0 和 Action1 在 API 中使用最廣泛,但 RxJava 是提供了多個 ActionX 形式的接口 (例如 Action2, Action3) 的,它們可以被用以包裝不同的無返回值的方法。

舉例

打印字符串數組

String[] names = ...;
Observable.from(names)
    .subscribe(new Action1<String>() {
        @Override
        public void call(String name) {
            Log.d(tag, name);
        }
    });

由 id 取得圖片并顯示

//由指定的一個 drawable 文件 id drawableRes 取得圖片,并顯示在 ImageView 中,并在出現異常的時候打印 Toast 報錯:
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
    @Override
    public void call(Subscriber<? super Drawable> subscriber) {
        Drawable drawable = getTheme().getDrawable(drawableRes));
        subscriber.onNext(drawable);
        subscriber.onCompleted();
    }
}).subscribe(new Observer<Drawable>() {
    @Override
    public void onNext(Drawable drawable) {
        imageView.setImageDrawable(drawable);
    }

    @Override
    public void onCompleted() {
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
    }
});
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容

  • 我從去年開始使用 RxJava ,到現在一年多了。今年加入了 Flipboard 后,看到 Flipboard 的...
    Jason_andy閱讀 5,552評論 7 62
  • 文章轉自:http://gank.io/post/560e15be2dca930e00da1083作者:扔物線在正...
    xpengb閱讀 7,055評論 9 73
  • 前言我從去年開始使用 RxJava ,到現在一年多了。今年加入了 Flipboard 后,看到 Flipboard...
    占導zqq閱讀 9,174評論 6 151
  • 均衡創建近一年, 城鄉教育換新顏。 辦學水平上臺階, 古澠大地迎春天。 經費投入超以前, 硬件軟件均配全。 校園文...
    無聲之言閱讀 206評論 0 0
  • 十幾歲的年齡,漸漸發育的身體,略顯幼稚的思想,盲目追求所謂流行,這就是我正在經歷的青春,在經歷小學,初...
    _南鳶閱讀 213評論 0 0