RxJava 2.x 教程及源碼揭祕(二)RxJava+Retrofit及其他Retrofit應用場景

 

RxJava 的適用場景和使用方式

1. 與 Retrofit 的結合

Retrofit 是 Square 的一個著名的網絡請求庫。沒有用過 Retrofit 的可以選擇跳過這一小節也沒關係,我舉的每種場景都只是個例子,而且例子之間並無前後關聯,只是個拋磚引玉的作用,所以你跳過這裏看別的場景也可以的。

Retrofit 除了提供了傳統的 Callback 形式的 API,還有 RxJava 版本的 Observable 形式 API。下面我用對比的方式來介紹 Retrofit 的 RxJava 版 API 和傳統版本的區別。

以獲取一個 User 對象的接口作爲例子。使用Retrofit 的傳統 API,你可以用這樣的方式來定義請求:

@GET("/user")
public void getUser(@Query("userId") String userId, Callback<User> callback);

在程序的構建過程中, Retrofit 會把自動把方法實現並生成代碼,然後開發者就可以利用下面的方法來獲取特定用戶並處理響應:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        userView.setUser(user);
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
};

而使用 RxJava 形式的 API,定義同樣的請求是這樣的:

@GET("/user")
public Observable<User> getUser(@Query("userId") String userId);

使用的時候是這樣的:

getUser(userId)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

看到區別了嗎?

當 RxJava 形式的時候,Retrofit 把請求封裝進 Observable ,在請求結束後調用 onNext() 或在請求失敗後調用 onError()

對比來看, Callback 形式和 Observable 形式長得不太一樣,但本質都差不多,而且在細節上 Observable 形式似乎還比 Callback 形式要差點。那 Retrofit 爲什麼還要提供 RxJava 的支持呢?

因爲它好用啊!從這個例子看不出來是因爲這只是最簡單的情況。而一旦情景複雜起來, Callback 形式馬上就會開始讓人頭疼。比如:

假設這麼一種情況:你的程序取到的 User 並不應該直接顯示,而是需要先與數據庫中的數據進行比對和修正後再顯示。使用 Callback 方式大概可以這麼寫:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        processUser(user); // 嘗試修正 User 數據
        userView.setUser(user);
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
};

有問題嗎?

很簡便,但不要這樣做。爲什麼?因爲這樣做會影響性能。數據庫的操作很重,一次讀寫操作花費 10~20ms 是很常見的,這樣的耗時很容易造成界面的卡頓。所以通常情況下,如果可以的話一定要避免在主線程中處理數據庫。所以爲了提升性能,這段代碼可以優化一下:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        new Thread() {
            @Override
            public void run() {
                processUser(user); // 嘗試修正 User 數據
                runOnUiThread(new Runnable() { // 切回 UI 線程
                    @Override
                    public void run() {
                        userView.setUser(user);
                    }
                });
            }).start();
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
};

性能問題解決,但……這代碼實在是太亂了,迷之縮進啊!雜亂的代碼往往不僅僅是美觀問題,因爲代碼越亂往往就越難讀懂,而如果項目中充斥着雜亂的代碼,無疑會降低代碼的可讀性,造成團隊開發效率的降低和出錯率的升高。

這時候,如果用 RxJava 的形式,就好辦多了。 RxJava 形式的代碼是這樣的:

getUser(userId)
    .doOnNext(new Action1<User>() {
        @Override
        public void call(User user) {
            processUser(user);
        })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

後臺代碼和前臺代碼全都寫在一條鏈中,明顯清晰了很多。

再舉一個例子:假設 /user 接口並不能直接訪問,而需要填入一個在線獲取的 token ,代碼應該怎麼寫?

Callback 方式,可以使用嵌套的 Callback

@GET("/token")
public void getToken(Callback<String> callback);

@GET("/user")
public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback);

...

getToken(new Callback<String>() {
    @Override
    public void success(String token) {
        getUser(token, userId, new Callback<User>() {
            @Override
            public void success(User user) {
                userView.setUser(user);
            }

            @Override
            public void failure(RetrofitError error) {
                // Error handling
                ...
            }
        };
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
});

倒是沒有什麼性能問題,可是迷之縮進毀一生,你懂我也懂,做過大項目的人應該更懂。

而使用 RxJava 的話,代碼是這樣的:

@GET("/token")
public Observable<String> getToken();

@GET("/user")
public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);

...

getToken()
    .flatMap(new Func1<String, Observable<User>>() {
        @Override
        public Observable<User> onNext(String token) {
            return getUser(token, userId);
        })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

用一個 flatMap() 就搞定了邏輯,依然是一條鏈。看着就很爽,是吧?

2016/03/31 更新,加上我寫的一個 Sample 項目:
rengwuxian RxJava Samples

好,Retrofit 部分就到這裏。

2. RxBinding

RxBinding 是 Jake Wharton 的一個開源庫,它提供了一套在 Android 平臺上的基於 RxJava 的 Binding API。所謂 Binding,就是類似設置 OnClickListener 、設置 TextWatcher 這樣的註冊綁定對象的 API。

舉個設置點擊監聽的例子。使用 RxBinding ,可以把事件監聽用這樣的方法來設置:

Button button = ...;
RxView.clickEvents(button) // 以 Observable 形式來反饋點擊事件
    .subscribe(new Action1<ViewClickEvent>() {
        @Override
        public void call(ViewClickEvent event) {
            // Click handling
        }
    });

看起來除了形式變了沒什麼區別,實質上也是這樣。甚至如果你看一下它的源碼,你會發現它連實現都沒什麼驚喜:它的內部是直接用一個包裹着的 setOnClickListener() 來實現的。然而,僅僅這一個形式的改變,卻恰好就是 RxBinding 的目的:擴展性。通過 RxBinding 把點擊監聽轉換成 Observable 之後,就有了對它進行擴展的可能。擴展的方式有很多,根據需求而定。一個例子是前面提到過的 throttleFirst() ,用於去抖動,也就是消除手抖導致的快速連環點擊:

RxView.clickEvents(button)
    .throttleFirst(500, TimeUnit.MILLISECONDS)
    .subscribe(clickAction);

如果想對 RxBinding 有更多瞭解,可以去它的 GitHub 項目 下面看看。

3. 各種異步操作

前面舉的 RetrofitRxBinding 的例子,是兩個可以提供現成的 Observable 的庫。而如果你有某些異步操作無法用這些庫來自動生成 Observable,也完全可以自己寫。例如數據庫的讀寫、大圖片的載入、文件壓縮/解壓等各種需要放在後臺工作的耗時操作,都可以用 RxJava 來實現,有了之前幾章的例子,這裏應該不用再舉例了。

4. RxBus

RxBus 名字看起來像一個庫,但它並不是一個庫,而是一種模式,它的思想是使用 RxJava 來實現了 EventBus ,而讓你不再需要使用 Otto 或者 GreenRobot 的 EventBus。至於什麼是 RxBus,可以看這篇文章。順便說一句,Flipboard 已經用 RxBus 替換掉了 Otto ,目前爲止沒有不良反應。

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