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的抽象類:Subscriber。Subscriber對Observer接口進行了一些擴展,但他們的基本使用方式是完全一樣的:
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再使用。所以如果只想使用基本功能,選擇Observer和Subscriber是完全一樣的。它們的區別對于使用者來說主要有兩點:
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被訂閱的時候,OnSubscribe和call方法會自動被調用,事件序列就會依照設定依次觸發。
這個例子很簡單:事件的內容是字符串,而不是一些復雜的對象;事件的內容是已經定好了的,而不像有的觀察者模式一樣是待確定的(例如網絡請求的結果在請求返回之前是未知的);所有事件在一瞬間被全部發送出去,而不是夾雜一些確定或不確定的時間間隔或者經過某種觸發器來觸發的。總之,這個例子看起來毫無實用價值。但這是為了便于說明,實質上只要你想,各種各樣的事件發送規則你都可以自己來寫。至于具體怎么做,后面都會講到,但現在不行。只有把基礎原理先說明白了,上層的運用才能更容易說清楚。
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(訂閱)
創建了Observable和Observer之后,再用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();
}
});