Retrofit與RXJava整合(轉)

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

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

@GET("/user")
public void getUser(@Query("userId") String userId, Callback<User> callback);
1
2
在程序的構建過程當中, Retrofit 會把自動把方法實現並生成代碼,而後開發者就能夠利用下面的方法來獲取特定用戶並處理響應:數據庫

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

@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
1
2
3
4
5
6
7
8
9
10
11
12
而使用 RxJava 形式的 API,定義一樣的請求是這樣的:json

@GET("/user")
public Observable<User> getUser(@Query("userId") String userId);
1
2
使用的時候是這樣的:設計模式

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

@Override
public void onCompleted() {
}ide

@Override
public void onError(Throwable error) {
// Error handling
...
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
看到區別了嗎?函數式編程

當 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
...
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
有問題嗎?

很簡便,但不要這樣作。爲何?由於這樣作會影響性能。數據庫的操做很重,一次讀寫操做花費 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
...
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
性能問題解決,但……這代碼實在是太亂了,迷之縮進啊!雜亂的代碼每每不只僅是美觀問題,由於代碼越亂每每就越難讀懂,而若是項目中充斥着雜亂的代碼,無疑會下降代碼的可讀性,形成團隊開發效率的下降和出錯率的升高。

這時候,若是用 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
...
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
其中

doOnNext()的執行在onNext()以前,對數據進行相關處理。doOnNext在哪個線程處理,暫時不明。

參考連接

RxJava操做符doOnNext - 享受技術帶來的快樂! - 博客頻道 - CSDN.NET

Rxjava中的doOnNext的做用和在哪裏執行 - u010746364的博客 - 博客頻道 - CSDN.NET

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

再舉一個例子:假設 /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
...
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
卻是沒有什麼性能問題,但是迷之縮進毀一輩子,你懂我也懂,作過大項目的人應該更懂。

而使用 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
...
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
用一個 flatMap() 就搞定了邏輯,依然是一條鏈。看着就很爽,是吧?

RxJava配合Retrofit2.0使用
新的Retrofit2.0簡直就是設計模式的教科書典範,同時對Rx的支持也更加友好,本例子爲查詢ip獲取地理信息,並過濾掉失敗信息

//使用Rxjava配合Retrofit解析json數據,注意這裏全是電腦運行的,沒有分開線程訂閱
public static void main(String[] args) throws Exception{
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new LoggingInterceptor());//log for okhttp

Retrofit retrofit = new Retrofit.Builder().baseUrl(IPService.END).client(client)
.addConverterFactory(GsonConverterFactory.create())//對Response進行adapter轉換
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//對轉換後的數據進行再包裝
.build();

retrofit.create(IPService.class)//動態代理生成class
//直接操做json數據,這裏可不是一個好的習慣,真正應該是DTO對象的
.getIPInfo("58.19.239.11")
.filter(jsonObject -> jsonObject.get("code").getAsInt()==0)
//轉換數據類型
.map(jsonObject1 -> jsonObject1.get("data"))
//輸出結果
.subscribe(System.out::println);
}

//retrofit定義的接口
interface IPService {
String END = "http://ip.taobao.com";
//建議寫成dto對象,博主只是爲了演示filter就把這裏JsonObject了
@GET("/service/getIpInfo.php") Observable<JsonObject> getIPInfo(@Query("ip") String ip);
}

/**
* Retrofit2.0已經把網絡部分剝離了,因此須要本身實現Log
*/
static class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();

long t1 = System.nanoTime();
System.out.println(
String.format("Sending request %s on %s%n%s", request.url(), chain.connection(),
request.headers()));

Response response = chain.proceed(request);

long t2 = System.nanoTime();
System.out.println(
String.format("Received response for %s in %.1fms%n%s", response.request().url(),
(t2 - t1) / 1e6d, response.headers()));

return response;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
源代碼

rengwuxian RxJava Samples

參考連接

給 Android 開發者的 RxJava 詳解

相關文章
相關標籤/搜索