深入理解RxJava中的觀察者模式

akiyama發表於2018-01-20

對於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 {
                // 結束方法
            }
        });
複製程式碼

使用輸入輸出流表示

image

上面程式碼省略了對資料的變換和組合操作,做最基礎的程式碼分析。 分析上面的過程發現

new ObservableOnSubscribe()

對應的是Observable的可觀察物件;

new Consumer()

對應的是觀察者物件,Rx在觀察者的基礎上擴充套件了2個物件 new Consumer() / new Action() 分別用於處理異常和一次任務完成的回撥處理;使用了泛型的方式來處理不同可觀察物件的資料傳遞。

從程式碼層面來分析他是如何把各個物件組合成觀察者模式的: 大概的圖示如下:

image

為什麼圖這樣的?

  • 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去主動拉取事件資訊,而不是可觀察物件主動推動呢?是否有違觀察者模式的定義?

觀察者模式的定義和變種

聊到這裡必須理清一下什麼是觀察者模式,觀察者模式真的有我們所理解的這麼簡單嗎?我們先看看維基百科關於觀察者模式的簡潔

觀察者模式是軟體設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實時事件處理系統。

image

注意這一句話“並且在它本身的狀態改變時主動發出通知”,這是官方的設計模式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方法的兩層含義需要結合原始碼仔細體會,然後在實踐中不斷的加深理解,才能做到融會貫通。

相關文章