對於Android開發者來說,RxJava應該是不陌生的,如果讀者不瞭解RxJava是什麼,推薦兩篇文章給大家
通過這兩篇文章相信你能對RxJava有一個初步的瞭解,這篇文章不打算介紹RxJava的具體使用方式,因為我覺得使用方式在文件中其實寫的很清楚,這些屬於“術”的層面,站在更高層次去理解RxJava的原理和核心內容才是“道”,只有“道”和“術”相結合才能真正的掌握RxJava,本篇文章介紹RxJava中的觀察者模式。
RxJava中觀察者模式
首先,我們來對RxJava做一個簡單的定義和總結,來說明RxJava是什麼,引用官方文件的一句話
Rx = Observables + LINQ + Schedulers
Rx本質上是一種程式設計模型,這種模型具有以下三個特性:
- Observables 使用觀察者模式來實現
- LINQ 具有LINQ的特性,最主要其實使用了Lambda表示式
- Schedulers 執行緒排程器
而RxJava 本質上就是使用Java語言實現了Rx的程式設計模型,通過RxJava解決了Android開發中的回撥地獄,執行緒切換,最重要的是讓業務的可擴充套件性更強,業務程式碼書寫更規範。 今天我們來聊聊Rx模型的第一個特性-觀察者模式。
要想理解RxJava中的觀察者模式我們先了解一下RxJava中的幾個簡單的概念:
- Observer/Subscriber
- Observable
Observer: 英文翻譯為觀察者,顧名思義它是一個觀察者物件(Observer)或者稱為訂閱者(Subscriber),A物件訂閱了某個事件,A物件觀察了某個事件。A就是觀察者。
Observable 既然有觀察者,那必然有被觀察者,Observable就是被觀察者,或者更專業一點說:可觀察事物件(Observable)
一個觀察者(Observer)訂閱一個可觀察物件(Observable),觀察者對Observable發射的資料或資料序列作出響應。這句話其實也解釋了為什麼Rx被稱為響應式程式設計。
舉一個很簡單的例子來對RxJava是如何串聯這幾個物件做到響應式程式設計的,
假設我們需要訂閱A地的天氣狀況,當A地天氣發生變化或者氣溫下降的時候通知到對應的訂閱者。
RxJava2中是這樣處理的,
Observable.create(new ObservableOnSubscribe<Weather>() {
@Override
public void subscribe(ObservableEmitter<Weather> observableEmitter) throws Exception {
if (!observableEmitter.isDisposed()) {
Weather weather = WeatherHelper.getCityWeather("A");
if(weather != null){
observableEmitter.onNext(weather);
} else {
observableEmitter.onError(new Exception("獲取A地天氣失敗"));
}
observableEmitter.onComplete();
}
}
}).map(new Function<Weather, Weather>() {
@Override
public Weather apply(Weather weather) throws Exception {
// 各種變換
return weather;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Consumer<Weather>() {
@Override
public void accept(Weather weather) throws Exception {
// 處理成功獲取天氣後的回撥
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
// 處理成功獲取天氣失敗的回撥
}
}, new Action() {
@Override
public void run() throws Exception {
// 結束方法
}
});
複製程式碼
使用輸入輸出流表示
上面程式碼省略了對資料的變換和組合操作,做最基礎的程式碼分析。 分析上面的過程發現
new ObservableOnSubscribe()
對應的是Observable的可觀察物件;
new Consumer()
對應的是觀察者物件,Rx在觀察者的基礎上擴充套件了2個物件 new Consumer() / new Action() 分別用於處理異常和一次任務完成的回撥處理;使用了泛型的方式來處理不同可觀察物件的資料傳遞。
從程式碼層面來分析他是如何把各個物件組合成觀察者模式的: 大概的圖示如下:
為什麼圖這樣的?
- Observable物件建立了一個可觀察物件ObservableOnSubscribe
- 中間觀察物件資料做各種變換處理(這個程式碼中省略),本質上還是返回了一個ObservableOnSubscri物件
- subscribeOn和observeOn對執行緒做簡單的排程處理
- Observable物件subscribe了Observer
- 觀察者主動去拉取可觀察者的資料,
觀察者主動去拉取可觀察者的資料,這一點是從原始碼角度分析得出的,
@SchedulerSupport("none")
public final void subscribe(Observer<? super T> observer) {
ObjectHelper.requireNonNull(observer, "observer is null");
try {
observer = RxJavaPlugins.onSubscribe(this, observer);
ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");
this.subscribeActual(observer);
} catch (NullPointerException var4) {
throw var4;
} catch (Throwable var5) {
Exceptions.throwIfFatal(var5);
RxJavaPlugins.onError(var5);
NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
npe.initCause(var5);
throw npe;
}
}
複製程式碼
簡單來理解就是A().B().C().subscribe(xxx);A().B().C()依然返回了一個 Observable物件,最終執行了subscribe方法來,原始碼中可以看到
subscribeActual(observer);
方法最終拉取可觀察者的資料,也就是執行了ObservableOnSubscribe的subscribe方法。
至此,我們有發現有幾個問題無法解釋:
- 為什麼是Observable物件訂閱了Observer,傳統的觀察者模式不應該是Observer訂閱Observable嗎?
- 為什麼是Observer去主動拉取事件資訊,而不是可觀察物件主動推動呢?是否有違觀察者模式的定義?
觀察者模式的定義和變種
聊到這裡必須理清一下什麼是觀察者模式,觀察者模式真的有我們所理解的這麼簡單嗎?我們先看看維基百科關於觀察者模式的簡潔
觀察者模式是軟體設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實時事件處理系統。
注意這一句話“並且在它本身的狀態改變時主動發出通知”,這是官方的設計模式UML圖,實際上在觀察者模式中還存在著另外一種觀察模式,那就是拉取模型:
- 推模型,事件通知源主動發起訊息推送,訂閱者自動接收資料
- 拉模型,訂閱者主動去拉取事件通知
拉取模型和推送模型唯一的區別在於notify方法的觸發是依賴於訂閱者主動發起通知,事件源才會把資料推送過來的。在執行notify方法的時候,會主動請求事件源的資料:
@Override
public void notify(Subject subject) {
// 主動去事件源裡拿資料
State state = ((ConcreteSubjectA) subject).getState();
System.out.println(getName() + "觀察者狀態更新為:" + state);
}
複製程式碼
這就是推模型的實現;在Android中典型如訊息推動,如果App正常接受訊息,那麼訊息通知是推動過去的,如果App沒有啟動,那麼App會主動去訊息服務端拉取未接收的訊息。
理解RxJava中的觀察者模式
明白了觀察者模式的2種基本模型,我們來理解一下前面的2個問題
- 為什麼是Observable物件訂閱了Observer,傳統的觀察者模式不應該是Observer訂閱Observable嗎?
- 為什麼是Observer去主動拉取事件資訊,而不是可觀察物件主動推動呢?是否有違觀察者模式的定義?
第一層意義:subscribe方法本質上是一個註冊方法,所以這就解釋了第一個問題,並不是Observable物件訂閱了Observer,而是向Observable註冊了一個觀察者(Observer); 第二層意義:subscribe方法在註冊了一個觀察者的同時,向事件源拉進行訊息的拉取
@SchedulerSupport("none")
public final void subscribe(Observer<? super T> observer) {
ObjectHelper.requireNonNull(observer, "observer is null");
try {
observer = RxJavaPlugins.onSubscribe(this, observer);
ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");
this.subscribeActual(observer);
} catch (NullPointerException var4) {
throw var4;
} catch (Throwable var5) {
Exceptions.throwIfFatal(var5);
RxJavaPlugins.onError(var5);
NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
npe.initCause(var5);
throw npe;
}
}
複製程式碼
subscribeActual就是向事件源拉取訊息,類似於前面提到的getState()方法。只不過這個方法不直接返回值,而是通過Consumer回撥的方式返回。
至此,整個過程就非常清晰了:
建立一個事件源(creat方法)-》對資料來源做變換(Map..)-》通過subscribe方法註冊一個觀察者,並執行拉取資料操作-》執行緒變換-》通過回撥返回資料結果
總結
RxJava中對觀察者模式的理解是最關鍵的,理解了觀察者模式後,才能對RxJava的整個設計有本質的瞭解,知其然更要知其所以然,subscribe方法的兩層含義需要結合原始碼仔細體會,然後在實踐中不斷的加深理解,才能做到融會貫通。