
前言
最近研究很火的開源庫RxJava,看過很多國內(nèi)android工程師寫的介紹。例如,F(xiàn)lipboard的扔物線同學(xué)寫的《給 Android 開發(fā)者的 RxJava 詳解》,講解得非常通俗易懂。不過我覺得對(duì)于初學(xué)者,還是不夠直觀(可能是我比較蠢)。
本文重點(diǎn)將一些常用場(chǎng)景羅列出來,讓大家簡(jiǎn)單地入門RxJava。
RxJava是什么
RxJava的github官網(wǎng)第一句說到:
“RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.”
本人理解:“RxJava是一個(gè)實(shí)現(xiàn)java響應(yīng)式編程的庫,讓異步事件以序列的形式組織代碼?!?/p>
引入RxJava
在build.gradle在dependencies加入
dependencies {
...
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
}
異步網(wǎng)絡(luò)請(qǐng)求:
場(chǎng)景:異步網(wǎng)絡(luò)請(qǐng)求一個(gè)User數(shù)據(jù),并在TextView展示。
平常代碼:
TextView textView = ...;
Map<String, String> params = new HashMap<>();
params.put("user_id", userid);// 請(qǐng)求參數(shù)
UserHttp client = new UserHttp();
client.post("http://server.com/user", params, new CallBack<String>() { // 異步請(qǐng)求
@Override
protected void onSuccess(String result) { // 在UI線程回調(diào)
// 返回的字符串(通常是一個(gè)json),解析成User對(duì)象
User user = parse(result);
textView.setText(user.getName());
}
});
大概就是這樣子了吧,當(dāng)然一般都會(huì)再封裝一下。
用RxJava大概是這樣子:
TextView textView = ...;
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> subscriber) {// 下面subscribeOn(Schedulers.newThread()) 把這方法設(shè)定在新線程回調(diào)
Map<String, String> params = new HashMap<>();
params.put("user_id", userid);// 請(qǐng)求參數(shù)
UserHttp client = new UserHttp();
Response response = client.post("http://kkmike999.com/user", params);// 同步請(qǐng)求
if (response.status == 200) { // 請(qǐng)求成功
String result = response.getResult();
subscriber.onNext(result);
subscriber.onCompleted();
} else {
// 請(qǐng)求失敗
subscriber.onError(new Throwable(response.getMessage()));
}
}
})
.subscribeOn(Schedulers.newThread()) // 設(shè)置call(...)方法,在新線程回調(diào);
// 可封裝得更美觀 Observable<String> observable = UserHttp.create(userid);
observable
.observeOn(AndroidSchedulers.mainThread())// 讓下面onNext()、onError()、onComplete()在UI線程(主線程)回調(diào)
.subscribe(new Subscriber<String>() {
@Override
public void onNext(String result) { // 上面 subscriber.onNext(result)在這里回調(diào)
// 返回的字符串(通常是一個(gè)json),解析成User對(duì)象
User user = parse(result);
textView.setText(user.getName());
}
@Override
public void onError(Throwable e) {} // 上面subscriber.onError(new Throwable(msg))在這里回調(diào)
@Override
public void onCompleted() {}
});
“這是什么鬼,代碼量不是多了嗎?”(心中千萬只草泥馬奔騰......)
在這么簡(jiǎn)單的場(chǎng)景上,使用RxJava代碼量確實(shí)會(huì)變多。但是(掩飾),請(qǐng)求User、解析User數(shù)據(jù)的邏輯拆開,也可設(shè)定什么事件在什么線程執(zhí)行。subscribeOn(Schedulers.newThread())設(shè)定下面call(subscriber<String>)在新線程回調(diào),observeOn(AndroidSchedulers.mainThread())設(shè)定本行下面的函數(shù)onNext、onError、onCompleted都在主線程執(zhí)行。一行代碼切換事件回調(diào)的線程,是不是很牛逼?
關(guān)于subscribeOn()、observeOn()、Schedulers的解釋,參考《給 Android 開發(fā)者的 RxJava 詳解》。
異步請(qǐng)求&處理數(shù)據(jù),主線程UI操作
場(chǎng)景條件苛刻點(diǎn):User user = parse(result);是一個(gè)耗時(shí)操作,要求異步線程處理,setText()在主線程操作。這時(shí)應(yīng)該怎么處理?
平常代碼:
client.post("http://server.com/user", params, new CallBack<String>() { // 異步請(qǐng)求
@Override
protected void onSuccess(String result) { // 在UI線程回調(diào)
// 異步處理
new Thread(new Runnable(){
@Override
public void run(){
User user = parse(result);
// 主線程
runOnUiThread(new Runnable(){
@Override
public void run(){
textView.setText(user.getName());
}
});
}
}).start();
}
});
蛋蛋疼的感覺出來木有? 八百個(gè)回調(diào)?。?!這種狀況,業(yè)界稱為“Callback Hell(回調(diào)地獄)”.
用RxJava讓你爽爽:
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>(){
call(){...} // Thread1回調(diào)
})
.subscribeOn(Schedulers.newThread()) // 設(shè)置 新線程Thread1回調(diào)call(...)
observable
.map(new Func1<String, User>() {
@Override
public User call(String result) { // Thread1回調(diào)
User user = parse(result);
return user;
}
})
.observeOn(AndroidSchedulers.mainThread()) // 切換回主線程
.subscribe(new Subscriber<User>() {
@Override
public void onNext(User user) { // 主線程
textView.setText(user.getName());
}
});
沒有多重回調(diào)了!
map(new Func1<String, User>() {...}做了轉(zhuǎn)換工作,把string轉(zhuǎn)成User,交給Subscriber<User>處理。

事件流
RxJava最大特點(diǎn),就是以“事件流”(前言中“序列的形式”)寫代碼。
上文中 事件1 網(wǎng)絡(luò)請(qǐng)求call(Subscriber<String>...)、事件2 json->User map(new Func1<String,User>...)、事件3 textView.setText() ,是事件先后順序,自上而下寫代碼,而不像平常代碼那樣“嵌套回調(diào)”。
隨著業(yè)務(wù)日益復(fù)雜,代碼會(huì)變得臃腫。多數(shù)情況下工程師會(huì)把代碼挪到Utils、Manager、Controller里面,即使這樣,由于多線程、并發(fā),“嵌套回調(diào)”無可避免。使用RxJava可以解決“Callback Hell”,讓業(yè)務(wù)邏輯非常清晰,盡管讀代碼的人不知道內(nèi)部怎么實(shí)現(xiàn),但清楚每一個(gè)事件發(fā)生的順序,看代碼效率提高,做測(cè)試也容易。
這就是為什么要用RxJava的最大原因。
值得注意,事件流在調(diào)用subscribe()之后,才開始工作。
多線程處理
還是之前的場(chǎng)景:....先在Thread1把string轉(zhuǎn)成JSONObject,再在Thread2將JSONObject轉(zhuǎn)User... (此處為了演示故意把情景弄復(fù)雜)
observable.subscribeOn(Schedulers.newThread()) // 新線程Thread1
.map(new Func1<String,JSONObject>(){
@Override
public JSONObject call(String result) { // 在Thread1運(yùn)行
return new JSONObject(result);
}
})
.subscribeOn(Schedulers.newThread()) // 切換新線程Thread2
.map(new Func1< JSONObject, User>() {
@Override
public User call(JSONObject json) { // 在Thread2運(yùn)行
User user = parse(json); // 與之前的parse()函數(shù)不一樣了
return user;
}
})
.observeOn(AndroidSchedulers.mainThread()) // 切換回主線程
.subscribe(new Subscriber<User>() {
@Override
public void onNext(User user) { // 主線程
textView.setText(user.getName());
}
});
好屌對(duì)吧! 還有flatMap()可以做轉(zhuǎn)換,跟map()有區(qū)別,由于有點(diǎn)復(fù)雜,此處不列舉flatMap()。

加入進(jìn)度條
場(chǎng)景:....在請(qǐng)求開始前,顯示進(jìn)度條,數(shù)據(jù)處理完后,隱藏進(jìn)度條...
RxJava:
ProgressDialog progress = ...;
progress.show();
...
.observeOn(AndroidSchedulers.mainThread()) // 規(guī)定Subscriber在主線程回調(diào)
.subscribe(new Subscriber<List<String>>() { // 主線程
@Override
public void onNext(List<String> strings) {...}
@Override
public void onComplete() {
progress.dismiss();
}
}
onNext、onComplete被設(shè)置在主線程執(zhí)行,因此可以執(zhí)行ui相關(guān)代碼。
變種觀察者模式
RxJava說是觀察者,但實(shí)際用起來有出入(《給 Android 開發(fā)者的 RxJava 詳解》有解釋為什么),特別是命名會(huì)讓剛接觸的同學(xué)混淆。所以我不強(qiáng)調(diào)RxJava的觀察者模式,大家懂得使用后再體會(huì)。
總結(jié)
使用RxJava寫代碼,嵌套的邏輯、各線程回調(diào)...都會(huì)以‘序列’的形式(通常稱為‘流’)表現(xiàn),你不會(huì)在RxJava代碼看到嵌套回調(diào)(如果有,很可能是使用不當(dāng))?!鳌尨a更直觀,邏輯按事件處理順序,一步一步來。讀者非常清晰哪個(gè)事件在哪個(gè)線程執(zhí)行,這一點(diǎn)至關(guān)重要。
剛使用門檻稍高,但隨著業(yè)務(wù)復(fù)雜化,RxJava的優(yōu)勢(shì)日漸明顯。RxJava不一定提供最好的開發(fā)模式,但接觸之后,會(huì)體驗(yàn)到j(luò)ava另一層境界。
感謝
感謝撰寫《給 Android 開發(fā)者的 RxJava 詳解》的拋物線同學(xué),對(duì)我學(xué)習(xí)RxJava有很大幫助。還要業(yè)界為RxJava翻譯中文文檔的、寫各種使用文章的同學(xué),你們辛苦了!
demo: https://github.com/kkmike999/RxJavaAsynPostDemo
版權(quán)聲明:可自由轉(zhuǎn)載,必須寫上原文鏈接or原作者