瞭解RxJava之基礎(一)

原文鏈接:Grokking RxJava, Part 1: The Basics

RxJava最近在Android開發者中火了起來。唯一的問題是入門比較困難,尤其是當你來自命令式編程的世界,但是一旦理解它,你就會發現RxJava真是太棒了。

這裏僅僅是帶你瞭解RxJava。整個系列共四篇文章,我希望你通過閱讀之後對RxJava產生興趣並瞭解RxJava的原理。

0x00 基礎

響應式程序的核心是Observables和Subscribers。Observable發出一系列事件,Subscriber處理這些事件。

有對事件發射的模式。一個Observable可以發射任何事件(零個或者多個),直到結束或出錯。對於每一個Subscriber,一個Observable調用Subscriber.onNext()任意次數,隨後調用Subscriber.onComplete()Subscriber.onError()

這看起來很像標準的觀察者模式,但它在一個關鍵的方法上有所不同。Observables往往不啓動發射事件,直到有人顯式的訂閱這些事件。

0x01 Hello, World!

讓我們以一個具體的例子來看看這個框架。首先,讓我們創造一個基本的Observable:

Observable<String> myObservable = Observable.create(
    new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> sub) {
            sub.onNext("Hello, world!");
            sub.onCompleted();
        }
    }
);

我們的Observable發射字符串”Hello, world!”,然後完成。現在讓我們創建一個Subscriber消費這個數據:

Subscriber<String> mySubscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) { System.out.println(s); }

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }
};

這裏僅僅是打印Observable發射的每個字符串。
現在我們通過subscribe()使myObservable和mySubscriber建立連接:

myObservable.subscribe(mySubscriber);
// Outputs "Hello, world!"

當訂閱建立之後,myObservable調用mySubscriber的onNext和onComplete方法。mysubscriber輸出“Hello, world!”,然後終止。

0x02 簡化代碼

這裏有很多樣板代碼僅是要打印“Hello, world!”,這麼囉嗦就是想讓你瞭解發生了什麼。RxJava提供了許多便捷方式以簡化編程。

首先,讓我們簡化Observable創建,,RxJava對常見任務,有多個內置的操作符。在這種情況下,Observable.just()發射單個事件然後完成,就像上面的代碼:

Observable<String> myObservable =
    Observable.just("Hello, world!");

接下來,我們處理Subscriber中不需要的回調,在上面例子中我們只是讓Observable發送一個數據,而且我們並沒有覆寫onComplete()和onError(),所以我們用一個簡單的類定義在onNext()中做什麼:

Action1<String> onNextAction = new Action1<String>() {
    @Override
    public void call(String s) {
        System.out.println(s);
    }
};

Actions可以定義Subscriber的每一個動作,例如onNext(),onError,onComplete():

myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);

然而,我們只需要第一個參數,因此先忽略onError()和onComplete():

myObservable.subscribe(onNextAction);
// Outputs "Hello, world!"

現在,讓我們通過鏈式調用的方法擺脫那些變量:

Observable.just("Hello, world!")
    .subscribe(new Action1<String>() {
        @Override
        public void call(String s) {
              System.out.println(s);
        }
    });

最後,讓我們使用Java 8 的lambdas表達式避免這麼醜陋的Action1代碼:

Observable.just("Hello, world!")
    .subscribe(s -> System.out.println(s));

如果你使用Android(因此不能使用Java 8),我強烈推薦你使用retrolambda,它能極大了減少冗長的代碼。

0x03 變換

讓我們做的更有趣些。
假設我想要在”Hello, world!”後追加簽名,一種可能就是改變Observable,像這樣:

Observable.just("Hello, world! -Dan")
    .subscribe(s -> System.out.println(s));

這樣只能在你的Observable是在你的控制之下,但不能保證總在這樣的情況。假如你使用第三方庫,一個潛在的問題:Observable在多處使用,但是隻有一次需要附加簽名,怎麼做呢?

我們嘗試修改我們的Subscriber,怎麼樣?

Observable.just("Hello, world!")
    .subscribe(s -> System.out.println(s + " -Dan"));

這個答案也不盡人意,因爲我想要Subscribers儘量的簡單,Subscribers可能需要在主線程中運行。從概念層面,Subscribers應該是響應事件,而非修改事件。

那豈不是很酷,如果我可以在一些中間步驟改變“Hello, world!”?

0x04 使用變換操作符(Operators)

這裏不是對RxJava所有Operators的介紹,如果需要更多的瞭解請移步這裏

下面我要解決數據變換的問題,通過介於Observable與Subscriber之間的操作符。RxJava帶有大量的操作符集合,但我們目前最好只關注極少數。

對於這種情況,map()操作符可以用於變換被髮射的數據:

Observable.just("Hello, world!")
    .map(new Func1<String, String>() {
        @Override
        public String call(String s) {
            return s + " -Dan";
        }
    })
    .subscribe(s -> System.out.println(s));

我們再次通過lambda表達式簡化:

Observable.just("Hello, world!")
    .map(s -> s + " -Dan")
    .subscribe(s -> System.out.println(s));

很酷,不是嗎?我們的map()操作符變換髮射的數據並返回另外的Observable,我們可以鏈式調用map(),變換數據到Subscriber最終消費形式。

0x05 map()操作符(Operators)

map操作符有個有趣的方面,它不必返回與源Observable相同的返回類型
假定我的Subscriber對輸出原文本不感興趣,而是要輸出文本的哈希值:

Observable.just("Hello, world!")
    .map(new Func1<String, Integer>() {
        @Override
        public Integer call(String s) {
            return s.hashCode();
        }
    })
    .subscribe(i -> System.out.println(Integer.toString(i)));

有趣,我們開始用一個字符串,但最終我們的Subscriber收到一個整數,我們再次用lambda表達式簡化這段代碼:

Observable.just("Hello, world!")
    .map(s -> s.hashCode())
    .subscribe(i -> System.out.println(Integer.toString(i)));

如我之前所說,我們希望我們的Subscriber儘可能做的更少,我們用另一個map把字符串哈希轉會字符串:

Observable.just("Hello, world!")
    .map(s -> s.hashCode())
    .map(i -> Integer.toString(i))
    .subscribe(s -> System.out.println(s));

請看:我們的Observable和Subscriber都回到了他們原來的代碼,我們只是增加了一些轉換步驟。我們同樣可以添加我的簽名然後變換:

Observable.just("Hello, world!")
    .map(s -> s + " -Dan")
    .map(s -> s.hashCode())
    .map(i -> Integer.toString(i))
    .subscribe(s -> System.out.println(s));

0x06 那又怎樣?

在這一點上,你可能認爲,這些是簡單的代碼加入了很多花式。這時一個簡單的例子,但是你應該從中獲得兩種理念:

1.Observable和Subscriber可以做任何事情
Observable發射的事件可以來自是一個數據庫查詢,Subscriber用來顯示查詢結果;也可以來自屏幕上的點擊事件,Subscriber用來響應點擊事件;Observable可以是一個網絡請求返回的數據流,Subscriber用來向硬盤寫入結果。

這是一個可以處理任何問題的通用框架。

2.Observable和Subscriber是獨立於中間的變換過程的
在Observable和Subscriber中間可以鏈式調用很多map操作符,該系統具有高度的可組合性:它很容易操縱數據。只要操作符數據輸入輸出都正確,就可以一直鏈式調用下去。

結合以上兩個主要的想法,你可以看到一個具有很大的潛力的系統,雖然當前我們只知道map操作符,這嚴重限制了我們的能力。在第2部分,我們將暢遊RxJava提供的大量操作符。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章