首先拋出一個問題:如果是一個新用戶, 必須先註冊, 等註冊成功之後再自動登錄該怎麼做呢.
private void login() {
api.login(new LoginRequest())
.subscribeOn(Schedulers.io()) //在IO線程進行網絡請求
.observeOn(AndroidSchedulers.mainThread()) //回到主線程去處理請求結果
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse loginResponse) throws Exception {
Toast.makeText(MainActivity.this, "登錄成功", Toast.LENGTH_SHORT).show();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "登錄失敗", Toast.LENGTH_SHORT).show();
}
});
}
private void register() {
api.register(new RegisterRequest())
.subscribeOn(Schedulers.io()) //在IO線程進行網絡請求
.observeOn(AndroidSchedulers.mainThread()) //回到主線程去處理請求結果
.subscribe(new Consumer<RegisterResponse>() {
@Override
public void accept(RegisterResponse registerResponse) throws Exception {
Toast.makeText(MainActivity.this, "註冊成功", Toast.LENGTH_SHORT).show();
login(); //註冊成功, 調用登錄的方法
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "註冊失敗", Toast.LENGTH_SHORT).show();
}
});
}
讓我們用一種更優雅的方式來解決這個問題.
Map
map是RxJava中最簡單的一個變換操作符了, 它的作用就是對上游發送的每一個事件應用一個函數, 使得每一個事件都按照指定的函數去變化. 用事件圖表示如下:
圖中map中的函數作用是將圓形事件轉換爲矩形事件, 從而導致下游接收到的事件就變爲了矩形.用代碼來表示這個例子就是:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Exception {
return "This is result " + integer;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
在上游我們發送的是數字類型, 而在下游我們接收的是String類型, 中間起轉換作用的就是map操作符, 運行結果爲:
D/TAG: This is result 1
D/TAG: This is result 2
D/TAG: This is result 3
通過Map, 可以將上游發來的事件轉換爲任意的類型, 可以是一個Object, 也可以是一個集合, 如此強大的操作符你難道不想試試?
接下來我們來看另外一個廣爲人知的操作符flatMap.
FlatMap
flatMap是一個非常強大的操作符, 先用一個比較難懂的概念說明一下:
FlatMap
將一個發送事件的上游Observable變換爲多個發送事件的Observables,然後將它們發射的事件合併後放進一個單獨的Observable裏.
這句話比較難以理解, 我們先通俗易懂的圖片來詳細的講解一下, 首先先來看看整體的一個圖片:
先看看上游, 上游發送了三個事件, 分別是1,2,3, 注意它們的顏色.
中間flatMap的作用是將圓形的事件轉換爲一個發送矩形事件和三角形事件的新的上游Observable.
還是不能理解? 別急, 再來看看分解動作:
concatMap
.Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).flatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("I am value " + integer);
}
return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
如代碼所示, 我們在flatMap中將上游發來的每個事件轉換爲一個新的發送三個String事件的水管, 爲了看到flatMap結果是無序的,所以加了10毫秒的延時, 來看看運行結果吧:
D/TAG: I am value 1
D/TAG: I am value 1
D/TAG: I am value 1
D/TAG: I am value 3
D/TAG: I am value 3
D/TAG: I am value 3
D/TAG: I am value 2
D/TAG: I am value 2
D/TAG: I am value 2
這裏也簡單說一下concatMap
吧, 它和flatMap的作用幾乎一模一樣, 只是它的結果是嚴格按照上游發送的順序來發送的, 來看個代碼吧:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).concatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("I am value " + integer);
}
return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
只是將之前的flatMap改爲了concatMap, 其餘原封不動, 運行結果如下:
D/TAG: I am value 1
D/TAG: I am value 1
D/TAG: I am value 1
D/TAG: I am value 2
D/TAG: I am value 2
D/TAG: I am value 2
D/TAG: I am value 3
D/TAG: I am value 3
D/TAG: I am value 3
實踐
學習了FlatMap操作符, 我們就可以回答文章開頭提出的那個問題了.
如何優雅的解決嵌套請求, 只需要用flatMap轉換一下就行了.
先回顧一下上一節的請求接口:
public interface Api {
@GET
Observable<LoginResponse> login(@Body LoginRequest request);
@GET
Observable<RegisterResponse> register(@Body RegisterRequest request);
}
可以看到登錄和註冊返回的都是一個上游Observable, 而我們的flatMap操作符的作用就是把一個Observable轉換爲另一個Observable, 因此結果就很顯而易見了:
api.register(new RegisterRequest()) //發起註冊請求
.subscribeOn(Schedulers.io()) //在IO線程進行網絡請求
.observeOn(AndroidSchedulers.mainThread()) //回到主線程去處理請求註冊結果
.doOnNext(new Consumer<RegisterResponse>() {
@Override
public void accept(RegisterResponse registerResponse) throws Exception {
//先根據註冊的響應結果去做一些操作
}
})
.observeOn(Schedulers.io()) //回到IO線程去發起登錄請求
.flatMap(new Function<RegisterResponse, ObservableSource<LoginResponse>>() {
@Override
public ObservableSource<LoginResponse> apply(RegisterResponse registerResponse) throws Exception {
return api.login(new LoginRequest());
}
})
.observeOn(AndroidSchedulers.mainThread()) //回到主線程去處理請求登錄的結果
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse loginResponse) throws Exception {
Toast.makeText(MainActivity.this, "登錄成功", Toast.LENGTH_SHORT).show();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "登錄失敗", Toast.LENGTH_SHORT).show();
}
});
注:本文總結自Season_zlc的給初學者的RxJava2.0教程