整理自:
http://blog.csdn.net/whuhan2013/article/details/51635653
http://gank.io/post/56e80c2c677659311bed9841
這篇文章從一個(gè)側(cè)面反映rxJava的優(yōu)越性,主要講解如何將rxJava與Retrofit2這兩個(gè)現(xiàn)在熱門的框架整合使用。
在看這篇文章之前,請(qǐng)先了解基本的rxJava和Retrofit2的知識(shí)
一、使用rxJava&Retrofit的好處
先讓我們看一下如果單純使用Retrofit會(huì)有什么問題:
-
復(fù)雜邏輯很難實(shí)現(xiàn):
比如:你的程序取到的 User 并不應(yīng)該直接顯示,而是需要先與數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行比對(duì)和修正后再顯示。使用 Callback 方式大概可以這么寫:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
processUser(user); // 嘗試修正 User 數(shù)據(jù)
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
很簡(jiǎn)便,但不要這樣做。為什么?因?yàn)檫@樣做會(huì)影響性能。數(shù)據(jù)庫的操作很重,一次讀寫操作花費(fèi) 10~20ms 是很常見的,這樣的耗時(shí)很容易造成界面的卡頓。所以通常情況下,如果可以的話一定要避免在主線程中處理數(shù)據(jù)庫。所以為了提升性能,這段代碼可以優(yōu)化一下:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
new Thread() {
@Override
public void run() {
processUser(user); // 嘗試修正 User 數(shù)據(jù)
runOnUiThread(new Runnable() { // 切回 UI 線程
@Override
public void run() {
userView.setUser(user);
}
});
}).start();
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
性能問題解決,但……這代碼實(shí)在是太亂了,迷之縮進(jìn)?。‰s亂的代碼往往不僅僅是美觀問題,因?yàn)榇a越亂往往就越難讀懂,而如果項(xiàng)目中充斥著雜亂的代碼,無疑會(huì)降低代碼的可讀性,造成團(tuán)隊(duì)開發(fā)效率的降低和出錯(cuò)率的升高。
這時(shí)候,如果用 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
...
}
});
代碼中oOnNext()的執(zhí)行在onNext()之前,對(duì)數(shù)據(jù)進(jìn)行相關(guān)處理。doOnNext具體的使用方法不是本文討論的重點(diǎn)。
-
ProgressDialog的show方法應(yīng)該在哪調(diào)用:
這個(gè)問題如果不用rxJava,解決起來也很容易:只要在調(diào)用Retrofit之前寫一遍show()的代碼,但是這樣不容易操控整個(gè)網(wǎng)絡(luò)請(qǐng)求的邏輯(這個(gè)我沒嘗試過,暫時(shí)這么認(rèn)為)。但是這個(gè)如果用rxJava解決的話就很簡(jiǎn)單,不在這里單講了,可以看下面的代碼。
二、在Retrofit上添加rxJava
- 添加Rxjava:
先在Gradle中添加依賴。
接著在創(chuàng)建Retrofit的過程中添加如下代碼:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
注意第四行,這樣一來我們定義的service返回值就不在是一個(gè)Call了,而是一個(gè)Observable
- 修改接口:
public interface MovieService {
@GET("top250")
Observable<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}
- 修改getMovie:
//進(jìn)行網(wǎng)絡(luò)請(qǐng)求
private void getMovie(){
String baseUrl = "https://api.douban.com/v2/movie/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
MovieService movieService = retrofit.create(MovieService.class);
movieService.getTopMovie(0, 10)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MovieEntity>() {
@Override
public void onCompleted() {
Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
resultTV.setText(e.getMessage());
}
@Override
public void onNext(MovieEntity movieEntity) {
resultTV.setText(movieEntity.toString());
}
});
}
三、封裝上述代碼
創(chuàng)建一個(gè)對(duì)象HttpMethods
public class HttpMethods {
public static final String BASE_URL = "https://api.douban.com/v2/movie/";
private static final int DEFAULT_TIMEOUT = 5;
private Retrofit retrofit;
private MovieService movieService;
//構(gòu)造方法私有
private HttpMethods() {
//手動(dòng)創(chuàng)建一個(gè)OkHttpClient并設(shè)置超時(shí)時(shí)間
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
movieService = retrofit.create(MovieService.class);
}
//在訪問HttpMethods時(shí)創(chuàng)建單例
private static class SingletonHolder{
private static final HttpMethods INSTANCE = new HttpMethods();
}
//獲取單例
public static HttpMethods getInstance(){
return SingletonHolder.INSTANCE;
}
/**
* 用于獲取豆瓣電影Top250的數(shù)據(jù)
* @param subscriber 由調(diào)用者傳過來的觀察者對(duì)象
* @param start 起始位置
* @param count 獲取長(zhǎng)度
*/
public void getTopMovie(Subscriber<MovieEntity> subscriber, int start, int count){
movieService.getTopMovie(start, count)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
}
用一個(gè)單例來封裝該對(duì)象,在構(gòu)造方法中創(chuàng)建Retrofit和對(duì)應(yīng)的Service。 如果需要訪問不同的基地址,那么你可能需要?jiǎng)?chuàng)建多個(gè)Retrofit對(duì)象,或者干脆根據(jù)不同的基地址封裝不同的HttpMethod類。
我們回頭再來看MainActivity中的getMovie方法:
private void getMovie(){
subscriber = new Subscriber<MovieEntity>() {
@Override
public void onCompleted() {
Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
resultTV.setText(e.getMessage());
}
@Override
public void onNext(MovieEntity movieEntity) {
resultTV.setText(movieEntity.toString());
}
};
HttpMethods.getInstance().getTopMovie(subscriber, 0, 10);
}
其中subscriber是MainActivity的成員變量。