為什么要用Rxjava?沒看出Rxjava到底解決了我們什么問題。一門技術或框架只有解決了實際問題,我們才能體會它的美妙,所以我們直面開發中的真實場景。
先來回顧下上篇我們使用Retrofit請求時的方法:
@GET("top250")
Call<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
getTopMovie(0, 10).enqueue(new Callback<MovieEntity>() {
@Override
public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) {
resultTV.setText(response.body().toString());
}
@Override
public void onFailure(Call<MovieEntity> call, Throwable t) {
resultTV.setText(t.getMessage());
}
});
現在我們使情景復雜起來,比如獲取到MovieEntity后,需要先跟數據庫中的數據對比后,再進行顯示,可以這樣寫:
getTopMovie(0, 10).enqueue(new Callback<MovieEntity>() {
@Override
public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) {
/** 嘗試比較數據并更新 **/
processData(response.body);
resultTV.setText(response.body().toString());
}
@Override
public void onFailure(Call<MovieEntity> call, Throwable t) {
resultTV.setText(t.getMessage());
}
});
但是把數據庫的操作直接放在主線程里,會造成界面卡頓,為了提升性能,我們把它放到子線程里:
getTopMovie(0, 10).enqueue(new Callback<MovieEntity>() {
@Override
public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) {
new Thread() {
@Override
public void run() {
/** 嘗試比較數據并更新 **/
processData(response.body);
/** 切回主線程 **/
runOnUiThread(new Runnable() {
@Override
public void run() {
resultTV.setText(response.body().toString());
}
});
}).start();
}
@Override
public void onFailure(Call<MovieEntity> call, Throwable t) {
resultTV.setText(t.getMessage());
}
});
也許你早就對這種雜亂的流程代碼看不順眼了吧,忍了好久終于到今天~,那不妨試試到手的Rxjava:
getTopMovie(start, count)
.subscribeOn(Schedulers.io())
/** doOnNext()允許我們在執行onNext()方法之前做一些額外的事情;
* 這里它是運行在Schedulers.io,所以我們可以訪問數據庫;
* 它和onNext()方法是同步操作,即執行完doOnNext()方法后才去執行onNext()方法。
**/
.doOnNext(new Action1<HttpResult<List<Subject>>>() {
@Override
public void call(HttpResult<List<Subject>> subjects) {
/** 嘗試比較數據并修復數據 **/
processData(subjects);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<HttpResult<List<Subject>>>() {
@Override
public void onNext(HttpResult<List<Subject>> subjects) {
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
});
這樣一來,我們的代碼邏輯是不是清晰多了。如果我們的需求是請求完數據,存放到數據庫,并顯示在UI上,那么上面doOnNext()(關于doOnNext()的測試)和onNext()方法的同步就顯然不適用了,不過我們可以這樣來修改:
.doOnNext(new Action1<HttpResult<List<Subject>>>() {
@Override
public void call(HttpResult<List<Subject>> subjects) {
/** 這里我們使用Schedulers.io()創建非阻塞的版本,
這樣saveData()和onNext()就實現了異步操作 **/
Schedulers.io().createWorker().schedule(new Action0() {
@Override
public void call() {
/** 這里我們把數據存到數據庫里 **/
saveData(subjects);
}
});
}
})
再來看一個同步等待的場景:假設獲取電影top250的接口不能直接訪問,需要填入一個在線獲取的 token,此時該怎么辦?
@GET("/token")
public void getToken(Callback<String> callback);
@GET("top250")
Call<MovieEntity> getTopMovie(@Query("token") String token, @Query("start") int start, @Query("count") int count);
/** 先獲取token **/
getToken(new Callback<String>() {
@Override
public void onResponse(String token) {
/** 成功后再獲取電影列表數據 **/
getTopMovie(token, 0, 10, new Callback<MovieEntity>() {
@Override
public void onResponse(MovieEntity response) {
resultTV.setText(response.toString());
}
@Override
public void onFailure(RetrofitError error) {
}
};
}
@Override
public void onFailure(RetrofitError error) {
}
});
又是嵌套式的代碼結構,還是不爽不爽。
/** 首先調用getToken() **/
getToken()
/** flatMap()允許我們干預事件流程并返回一個Observable,即轉換為其他事件 **/
.flatMap(new Func1<String, Observable<HttpResult<List<Subject>>>>() {
@Override
public Observable<HttpResult<List<Subject>>> onNext(String token) {
/** 然后調用getTopMovie() **/
return getTopMovie(token, 0, 10);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<HttpResult<List<Subject>>>() {
@Override
public void onNext(HttpResult<List<Subject>> subjects) {
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
});
以上使用flatMap即用到了Rxjava的 變換 概念,所謂變換,就是將事件序列中的對象或整個序列進行加工處理,轉換成不同的事件或事件序列。通常可以使用map,flatMap,lift等方法來處理變換。
剛開始可能會覺得Rxjava的封裝比較抽象,使用起來比較生澀,但通過以上情景的討論,我們能夠看出Rxjava在代碼結構上本質的簡潔,通過練習多多上手,最終用在項目上才是學習、使用它的最好方式。