RxJava詳解(二)
說(shuō)好的簡(jiǎn)潔呢?
上面這一部分,又是介紹、又是Hello World
、又是數(shù)據(jù)變換,但是你會(huì)發(fā)現(xiàn)然而并沒(méi)有什么卵用。說(shuō)好的簡(jiǎn)潔一點(diǎn)也木有體現(xiàn)出來(lái)。
下面我們會(huì)通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)進(jìn)行說(shuō)明。現(xiàn)在我們有這樣一個(gè)需求:
有一個(gè)服務(wù)提供了一些
API
來(lái)搜索整個(gè)網(wǎng)絡(luò)上的符合查詢關(guān)鍵字的所有貓的圖片。 每個(gè)圖片包含一個(gè)可愛(ài)程度的參數(shù)(一個(gè)整數(shù)值表示其可愛(ài)程度)。 我們的任務(wù)就是下載所有貓的列表并選擇最可愛(ài)的那個(gè),把它的圖片保存到本地。
Model
和API
下面是貓的數(shù)據(jù)結(jié)構(gòu)Cat
:
public class Cat implements Comparable<Cat>{
Bitmap image;
int cuteness;
@Override
public int compareTo(Cat another) {
return Integer.compare(cuteness, another.cuteness);
}
}
我們的API
會(huì)調(diào)用cat-sdk.jar
中堵塞式的接口。
public interface Api {
List<Cat> queryCats(String query);
Uri store(Cat cat);
}
看起來(lái)很清晰吧?當(dāng)然了,我們繼續(xù)寫(xiě)客戶端的業(yè)務(wù)邏輯.
public class CatsHelper {
Api api;
public Uri saveTheCutestCat(String query){
List<Cat> cats = api.queryCats(query);
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
return savedUri;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
通俗易懂、簡(jiǎn)單明了,非常酷的代碼。主要的函數(shù)saveTheCutestCat()
只包含了3個(gè)方法。使用這些方法然后等待方法執(zhí)行完并接受返回值就可以了。
非常簡(jiǎn)單、有效。接下來(lái)我們看一下這種方式的其他優(yōu)點(diǎn)。
組合
可以看到我們的saveTheCutestCat
由其他三個(gè)函數(shù)調(diào)用所組成的。我們通過(guò)函數(shù)來(lái)把一個(gè)大功能分割為每個(gè)容易理解的小功能。通過(guò)函數(shù)調(diào)用來(lái)組合使用這些小功能。使用和理解起來(lái)都相當(dāng)簡(jiǎn)單
異常傳遞
另外一個(gè)使用函數(shù)的好處就是方便處理異常。每個(gè)函數(shù)都可以通過(guò)拋出異常來(lái)結(jié)束運(yùn)行。該異常可以在拋出異常的函數(shù)里面處理,也可以在調(diào)用該函數(shù)的外面處理,所以我們無(wú)需每次都處理每個(gè)異常,我們可以在一個(gè)地方處理所有可能拋出的異常。
try{
List<Cat> cats = api.queryCats(query);
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
return savedUri;
} catch (Exception e) {
e.printStackTrace()
return someDefaultValue;
}
這樣,我們就可以處理這三個(gè)函數(shù)中所拋出的任何異常了。如果沒(méi)有try catch
語(yǔ)句,我們也可以把異常繼續(xù)傳遞下去。
向異步粗發(fā)
但是,現(xiàn)實(shí)世界中我們往往沒(méi)法等待。有些時(shí)候你沒(méi)法只使用阻塞調(diào)用。在Android
中你需要處理各種異步操作。
就那Android
的OnClickListener
接口來(lái)說(shuō)吧,如果你需要處理一個(gè)View
的點(diǎn)擊事件,你必須提供一個(gè)該Listener
的實(shí)現(xiàn)來(lái)處理用戶的點(diǎn)擊事件。下面來(lái)看看如何處理異步調(diào)用。
異步網(wǎng)絡(luò)調(diào)用
假設(shè)我們的cats-sdk.jar
使用了異步調(diào)用的API
來(lái)訪問(wèn)網(wǎng)絡(luò)資源,
這樣我們的新API
接口就變?yōu)檫@樣了:
public interface Api {
interface CatsQueryCallback {
void onCatListReceived(List<Cat> cats);
void onError(Exception e);
}
void queryCats(String query, CatsQueryCallback catsQueryCallback);
Uri store(Cat cat);
}
這樣我們查詢貓的操作就變?yōu)楫惒降牧耍?通過(guò)CatsQueryCallback
回調(diào)接口來(lái)結(jié)束查詢的數(shù)據(jù)和處理異常情況。
我們的業(yè)務(wù)邏輯也需要跟著改變一下:
public class CatsHelper {
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onQueryFailed(Exception e);
}
Api api;
public void saveTheCutestCat(String query, CutestCatCallback cutestCatCallback){
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
cutestCatCallback.onCutestCatSaved(savedUri);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onQueryFailed(e);
}
});
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
我們沒(méi)法讓saveTheCutestCat
函數(shù)返回一個(gè)值了, 我們需要一個(gè)回調(diào)接口來(lái)異步的處理結(jié)果。
這里我們?cè)龠M(jìn)一步,使用兩個(gè)異步操作來(lái)實(shí)現(xiàn)我們的功能,例如我們需要使用異步IO
來(lái)寫(xiě)文件。
public interface Api {
interface CatsQueryCallback {
void onCatListReceived(List<Cat> cats);
void onQueryFailed(Exception e);
}
interface StoreCallback{
void onCatStored(Uri uri);
void onStoreFailed(Exception e);
}
void queryCats(String query, CatsQueryCallback catsQueryCallback);
void store(Cat cat, StoreCallback storeCallback);
}
我們的helper
會(huì)變成:
public class CatsHelper {
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onError(Exception e);
}
Api api;
public void saveTheCutestCat(String query, CutestCatCallback cutestCatCallback){
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
Cat cutest = findCutest(cats);
api.store(cutest, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
cutestCatCallback.onCutestCatSaved(uri);
}
@Override
public void onStoreFailed(Exception e) {
cutestCatCallback.onError(e);
}
});
}
@Override
public void onQueryFailed(Exception e) {
cutestCatCallback.onError(e);
}
});
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
現(xiàn)在我們?cè)賮?lái)看看這部分代碼?還是之前那樣簡(jiǎn)單暴力?現(xiàn)在有太多的干擾代碼、匿名類(lèi),這簡(jiǎn)直是太恐怖了,但是他們的業(yè)務(wù)邏輯其實(shí)是一樣的,都是查詢貓的列表數(shù)據(jù),然后找出最可愛(ài)的貓并保存它的圖片。
上面說(shuō)好的組合功能沒(méi)有了,現(xiàn)在你沒(méi)法像阻塞操作一樣來(lái)組合調(diào)用每個(gè)功能了,異步操作中,每次你都必須通過(guò)回調(diào)接口來(lái)手工的處理結(jié)果。
上面說(shuō)好的異常處理也沒(méi)有了,異步代碼中的異常不會(huì)自動(dòng)傳遞,我們需要手動(dòng)的去重新傳遞。(onStoreFailed()
和onQueryFailed()
就是干這事的)
結(jié)果?
然后呢?我們可以怎么做?我們能不能使用無(wú)回調(diào)的模式?我們?cè)囍迯?fù)一下。
奔向更好的世界
通用的回調(diào)
如果我們仔細(xì)的觀察下回調(diào)接口,我們會(huì)發(fā)現(xiàn)它們的共性:
- 它們都有一個(gè)分發(fā)結(jié)果的方法(
onCutestCatSaved()
,onCatListReceived()
,onCatStored()
) - 它們中的絕大部分都有一個(gè)處理操作過(guò)程中異常的方法(
onError()
,onQueryFailed()
,onStoreFailed()
)
所以我們可以創(chuàng)建一個(gè)通用的回調(diào)來(lái)取代它們。但是我們無(wú)法修改api
的調(diào)用結(jié)構(gòu),我們只能創(chuàng)建一個(gè)包裹層的調(diào)用。
我們通用的回調(diào)如下:
public interface Callback<T> {
void onResult(T result);
void onError(Exception e);
}
我們創(chuàng)建一個(gè)ApiWrapper
類(lèi)來(lái)改變我們調(diào)用的結(jié)構(gòu):
public class ApiWrapper {
Api api;
public void queryCats(String query, Callback<List<Cat>> catsCallback){
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
catsCallback.onResult(cats);
}
@Override
public void onQueryFailed(Exception e) {
catsCallback.onError(e);
}
});
}
public void store(Cat cat, Callback<Uri> uriCallback){
api.store(cat, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
uriCallback.onResult(uri);
}
@Override
public void onStoreFailed(Exception e) {
uriCallback.onError(e);
}
});
}
}
這樣通過(guò)新的回調(diào)我們可以減少一次處理結(jié)果和異常的邏輯。
最終,我們的CatsHelper
如下:
public class CatsHelper{
ApiWrapper apiWrapper;
public void saveTheCutestCat(String query, Callback<Uri> cutestCatCallback){
apiWrapper.queryCats(query, new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> cats) {
Cat cutest = findCutest(cats);
apiWrapper.store(cutest, cutestCatCallback);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
好了,現(xiàn)在比之前的代碼稍微簡(jiǎn)單點(diǎn)了。但是我們能不能做的更好? 當(dāng)然可以!
保持參數(shù)和回調(diào)的分離性
看看這些新的異步操作(queryCats
,store
和saveTheCutestCat
)。這些函數(shù)都有同樣的模式。使用一些參數(shù)來(lái)調(diào)用這些函數(shù)(query,cat)
,同時(shí)還有一個(gè)回調(diào)接口作為參數(shù)。甚至,所有的異步操作都帶有一些常規(guī)參數(shù)和一個(gè)額外的回調(diào)接口參數(shù)。如果我們把他們分離開(kāi)會(huì)如何,讓每個(gè)異步操作只有一些常規(guī)參數(shù)而把返回一個(gè)臨時(shí)的對(duì)象來(lái)操作回調(diào)接口。
下面來(lái)試試看看這種方式能否有效。
如果我們返回一個(gè)臨時(shí)的對(duì)象作為異步操作的回調(diào)接口處理方式,我們需要先定義這個(gè)對(duì)象。由于對(duì)象遵守通用的行為(有一個(gè)回調(diào)接口參數(shù)),我們定義一個(gè)能用于所有操作的對(duì)象。我們稱之為AsyncJob
。
注意: 我非常想把這個(gè)名字稱之為
AsyncTask
。但是由于Android
系統(tǒng)已經(jīng)有個(gè)AsyncTask
類(lèi)了, 為了避免混淆,所以就用AsyncJob
了。
該對(duì)象如下:
public abstract class AsyncJob<T> {
public abstract void start(Callback<T> callback);
}
start()
函數(shù)有個(gè)Callback
回調(diào)接口參數(shù),并開(kāi)始執(zhí)行該操作。
ApiWrapper
修改為:
public class ApiWrapper {
Api api;
public AsyncJob<List<Cat>> queryCats(String query) {
return new AsyncJob<List<Cat>>() {
@Override
public void start(Callback<List<Cat>> catsCallback) {
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
catsCallback.onResult(cats);
}
@Override
public void onQueryFailed(Exception e) {
catsCallback.onError(e);
}
});
}
};
}
public AsyncJob<Uri> store(Cat cat) {
return new AsyncJob<Uri>() {
@Override
public void start(Callback<Uri> uriCallback) {
api.store(cat, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
uriCallback.onResult(uri);
}
@Override
public void onStoreFailed(Exception e) {
uriCallback.onError(e);
}
});
}
};
}
}
目前看起來(lái)還不錯(cuò)。現(xiàn)在可以使用AsyncJob.start()
來(lái)啟動(dòng)每個(gè)操作了。接下來(lái)我們修改CatsHelper
類(lèi):
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
return new AsyncJob<Uri>() {
@Override
public void start(Callback<Uri> cutestCatCallback) {
apiWrapper.queryCats(query)
.start(new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> cats) {
Cat cutest = findCutest(cats);
apiWrapper.store(cutest)
.start(new Callback<Uri>() {
@Override
public void onResult(Uri result) {
cutestCatCallback.onResult(result);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
};
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
看起來(lái)比前面一個(gè)版本更加復(fù)雜啊,這樣有啥好處啊?
這里其實(shí)我們返回的是一個(gè)AsyncJob
對(duì)象,該對(duì)象和客戶端代碼組合使用,這樣在Activity
或者Fragment
客戶端代碼中就可以操作這個(gè)返回的對(duì)象了。
代碼雖然目前看起來(lái)比較復(fù)雜,下面我們就來(lái)改進(jìn)一下。
分解
下面是流程圖:
(async) (sync) (async)
query ===========> List<Cat> -------------> Cat ==========> Uri
queryCats findCutest store
為了讓代碼具有可讀性,我們把這個(gè)流程分解為每個(gè)操作。同時(shí)我們?cè)龠M(jìn)一步假設(shè),如果一個(gè)操作是異步的,則每個(gè)調(diào)用該異步操作的函數(shù)也是異步的。例如:如果查詢貓是個(gè)異步操作,則找到最可愛(ài)的貓操作也是異步的。
因此,我們可以使用AsyncJob
來(lái)把這些操作分解為一些小的操作中。
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>() {
@Override
public void start(Callback<Cat> callback) {
catsListAsyncJob.start(new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> result) {
callback.onResult(findCutest(result));
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
@Override
public void start(Callback<Uri> cutestCatCallback) {
cutestCatAsyncJob.start(new Callback<Cat>() {
@Override
public void onResult(Cat cutest) {
apiWrapper.store(cutest)
.start(new Callback<Uri>() {
@Override
public void onResult(Uri result) {
cutestCatCallback.onResult(result);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
};
return storedUriAsyncJob;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
雖然代碼量多了,但是看起來(lái)更加清晰了。 嵌套的回調(diào)函數(shù)沒(méi)那么多層級(jí)了,異步操作的名字也更容易理解了(catsListAsyncJob
,cutestCatAsyncJob
, storedUriAsyncJob
)。
看起來(lái)還不錯(cuò),但是還可以更好。
簡(jiǎn)單的映射
先來(lái)看看我們創(chuàng)建 AsyncJob cutestCatAsyncJob 的代碼:
AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>() {
@Override
public void start(Callback<Cat> callback) {
catsListAsyncJob.start(new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> result) {
callback.onResult(findCutest(result));
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
這 16 行代碼中,只有一行代碼是我們的業(yè)務(wù)邏輯代碼:
findCutest(result)
其他的代碼只是為了啟動(dòng)AsyncJob
并接收結(jié)果和處理異常的干擾代碼。 但是這些代碼是通用的,我們可以把他們放到其他地方來(lái)讓我們更加專注業(yè)務(wù)邏輯代碼。
那么如何實(shí)現(xiàn)呢?需要做兩件事情:
- 通過(guò)
AsyncJob
獲取需要轉(zhuǎn)換的結(jié)果 - 轉(zhuǎn)換的函數(shù)
但是由于Java
的限制,無(wú)法把函數(shù)作為參數(shù),所以需要用一個(gè)接口(或者類(lèi))并在里面定義一個(gè)轉(zhuǎn)換函數(shù):
public interface Func<T, R> {
R call(T t);
}
灰常簡(jiǎn)單。 有兩個(gè)泛型類(lèi)型定義,T
代表參數(shù)的類(lèi)型;R
代表返回值的類(lèi)型。
當(dāng)我們把AsyncJob
的結(jié)果轉(zhuǎn)換為其他類(lèi)型的時(shí)候,我們需要把一個(gè)結(jié)果值映射為另外一種類(lèi)型,這個(gè)操作我們稱之為map
。 把該函數(shù)定義到AsyncJob
類(lèi)中比較方便,這樣就可以通過(guò)this
來(lái)訪問(wèn)AsyncJob
對(duì)象了。
public abstract class AsyncJob<T> {
public abstract void start(Callback<T> callback);
public <R> AsyncJob<R> map(Func<T, R> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override
public void start(Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
R mapped = func.call(result);
callback.onResult(mapped);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
}
看起來(lái)不錯(cuò), 現(xiàn)在的CatsHelper
如下:
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
@Override
public void start(Callback<Uri> cutestCatCallback) {
cutestCatAsyncJob.start(new Callback<Cat>() {
@Override
public void onResult(Cat cutest) {
apiWrapper.store(cutest)
.start(new Callback<Uri>() {
@Override
public void onResult(Uri result) {
cutestCatCallback.onResult(result);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
};
return storedUriAsyncJob;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
新創(chuàng)建的AsyncJob cutestCatAsyncJob()
的代碼只有6行,并且只有一層嵌套。
高級(jí)映射
但是AsyncJob storedUriAsyncJob()
看起來(lái)還是非常糟糕。 這里也能使用映射嗎? 下面就來(lái)試試吧!
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, Uri>() {
@Override
public Uri call(Cat cat) {
return apiWrapper.store(cat);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 將會(huì)導(dǎo)致無(wú)法編譯
// Incompatible types:
// Required: Uri
// Found: AsyncJob<Uri>
}
});
return storedUriAsyncJob;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
哎。。。 看起來(lái)沒(méi)這么簡(jiǎn)單啊, 下面修復(fù)返回的類(lèi)型再試一次:
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
AsyncJob<AsyncJob<Uri>> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriAsyncJob;
//^^^^^^^^^^^^^^^^^^^^^^^ 將會(huì)導(dǎo)致無(wú)法編譯
// Incompatible types:
// Required: AsyncJob<Uri>
// Found: AsyncJob<AsyncJob<Uri>>
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
這里我們只能拿到AsyncJob<AsyncJob>
。看來(lái)還需要更進(jìn)一步。我們需要壓縮一層AsyncJob
,把兩個(gè)異步操作當(dāng)做一個(gè)單一的異步操作來(lái)對(duì)待。
現(xiàn)在我們需要一個(gè)參數(shù)為AsyncJob
的map
轉(zhuǎn)換操作而不是R
。該操作類(lèi)似于map
,但是該操作會(huì)把嵌套的AsyncJob
壓縮為flatten
一層AsyncJob
. 我們稱之為flatMap
,實(shí)現(xiàn)如下:
public abstract class AsyncJob<T> {
public abstract void start(Callback<T> callback);
public <R> AsyncJob<R> map(Func<T, R> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override
public void start(Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
R mapped = func.call(result);
callback.onResult(mapped);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
public <R> AsyncJob<R> flatMap(Func<T, AsyncJob<R>> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override
public void start(Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
AsyncJob<R> mapped = func.call(result);
mapped.start(new Callback<R>() {
@Override
public void onResult(R result) {
callback.onResult(result);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
}
看起來(lái)有很多干擾代碼,但是還好這些代碼在客戶端代碼中并不會(huì)出現(xiàn)。 現(xiàn)在我們的CatsHelper
如下:
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.flatMap(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriAsyncJob;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
如果把匿名類(lèi)修改為Java 8
的lambdas
表達(dá)式(邏輯是一樣的,只是讓代碼看起來(lái)更清晰點(diǎn))就很容易發(fā)現(xiàn)了。
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(cats -> findCutest(cats));
AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.flatMap(cat -> apiWrapper.store(cat));
return storedUriAsyncJob;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
這樣看起來(lái)是不是就很清晰了。 這個(gè)代碼和剛剛開(kāi)頭的阻塞式代碼是不是非常相似:
public class CatsHelper {
Api api;
public Uri saveTheCutestCat(String query){
List<Cat> cats = api.queryCats(query);
Cat cutest = findCutest(cats);
Uri savedUri = api.store(cutest);
return savedUri;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
現(xiàn)在他們不僅邏輯是一樣的,語(yǔ)義上也是一樣的。 太棒了!
同時(shí)我們還可以使用組合操作,現(xiàn)在把兩個(gè)異步操作組合一起并返還另外一個(gè)異步操作。
異常處理也會(huì)傳遞到最終的回調(diào)接口中。
下面來(lái)看看RxJava
吧。
你沒(méi)必要把上面代碼應(yīng)用到您的項(xiàng)目中去, 這些簡(jiǎn)單的、線程不安全的代碼只是 RxJava
的一部分。
只有一些名字上的不同:
-
AsyncJob
等同于Observable
,不僅僅可以返回一個(gè)結(jié)果,還可以返回一系列的結(jié)果,當(dāng)然也可能沒(méi)有結(jié)果返回。 -
Callback
等同于Observer
,除了onNext(T t)
,onError(Throwable t)
以外,還有一個(gè)onCompleted()
函數(shù),該函數(shù)在結(jié)束繼續(xù)返回結(jié)果的時(shí)候通知Observable
。 -
abstract void start(Callback callback)
和Subscription subscribe(final Observer observer)
類(lèi)似,返回一個(gè)Subscription
,如果你不再需要后面的結(jié)果了,可以取消該任務(wù)。
下面是RxJava
版本的代碼:
public class ApiWrapper {
Api api;
public Observable<List<Cat>> queryCats(final String query) {
return Observable.create(new Observable.OnSubscribe<List<Cat>>() {
@Override
public void call(final Subscriber<? super List<Cat>> subscriber) {
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
subscriber.onNext(cats);
}
@Override
public void onQueryFailed(Exception e) {
subscriber.onError(e);
}
});
}
});
}
public Observable<Uri> store(final Cat cat) {
return Observable.create(new Observable.OnSubscribe<Uri>() {
@Override
public void call(final Subscriber<? super Uri> subscriber) {
api.store(cat, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
subscriber.onNext(uri);
}
@Override
public void onStoreFailed(Exception e) {
subscriber.onError(e);
}
});
}
});
}
}
public class CatsHelper {
ApiWrapper apiWrapper;
public Observable<Uri> saveTheCutestCat(String query) {
Observable<List<Cat>> catsListObservable = apiWrapper.queryCats(query);
Observable<Cat> cutestCatObservable = catsListObservable.map(new Func1<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return CatsHelper.this.findCutest(cats);
}
});
Observable<Uri> storedUriObservable = cutestCatObservable.flatMap(new Func1<Cat, Observable<? extends Uri>>() {
@Override
public Observable<? extends Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriObservable;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
把 Observable 替換為 AsyncJob 后 他們的代碼是一樣的。
結(jié)論
通過(guò)簡(jiǎn)單的轉(zhuǎn)換操作,我們可以把異步操作抽象出來(lái)。這種抽象的結(jié)果可以像操作簡(jiǎn)單的阻塞函數(shù)一樣來(lái)操作異步操作并組合異步操作。這樣我們就可以擺脫層層嵌套的回調(diào)接口了,并且不用手工的去處理每次異步操作的異常。
上面這個(gè)例子非常好,建議多看幾遍,加深理解,可能把這個(gè)例子放在這里并不太好,把它放到開(kāi)始講之前可能更容易理解,但是我覺(jué)得,介紹完概念、使用方法和基本的操作符后,我們可能并不能理解操作符的原理和作用。之前看完操作符原理后迷迷糊糊的狀態(tài)再來(lái)看這個(gè)例子會(huì)豁然開(kāi)朗。
這里也感謝牛逼的作者Yaroslav(也是RxAndroid
項(xiàng)目的一個(gè)重要參與者)能用這么牛逼的例子,講解的如此透徹。
如果嫌上面的代碼麻煩,可以通過(guò)下面的例子看:
假設(shè)有這樣一個(gè)需求:界面上有一個(gè)自定義的視圖imageCollectorView
,它的作用是顯示多張圖片,并能使用addImage(Bitmap)
方法來(lái)任意增加顯示的圖片。現(xiàn)在需要程序?qū)⒁粋€(gè)給出的目錄數(shù)組File[] folders
中每個(gè)目錄下的png
圖片都加載出來(lái)并顯示在imageCollectorView
中。需要注意的是,由于讀取圖片的這一過(guò)程較為耗時(shí),需要放在后臺(tái)執(zhí)行,而圖片的顯示則必須在UI
線程執(zhí)行。常用的實(shí)現(xiàn)方式有多種,我這里貼出其中一種:
new Thread() {
@Override
public void run() {
super.run();
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}
}.start();
而如果使用 RxJava ,實(shí)現(xiàn)方式是這樣的:
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
});
那位說(shuō)話了:『你這代碼明明變多了啊!簡(jiǎn)潔個(gè)毛啊!』大兄弟你消消氣,我說(shuō)的是邏輯的簡(jiǎn)潔,不是單純的代碼量少(邏輯簡(jiǎn)潔才是提升讀寫(xiě)代碼速度的必殺技對(duì)不?)。觀察一下你會(huì)發(fā)現(xiàn), RxJava
的這個(gè)實(shí)現(xiàn),是一條從上到下的鏈?zhǔn)秸{(diào)用,沒(méi)有任何嵌套,這在邏輯的簡(jiǎn)潔性上是具有優(yōu)勢(shì)的。當(dāng)需求變得復(fù)雜時(shí),這種優(yōu)勢(shì)將更加明顯(試想如果還要求只選取前10
張圖片,常規(guī)方式要怎么辦?如果有更多這樣那樣的要求呢?再試想,在這一大堆需求實(shí)現(xiàn)完兩個(gè)月之后需要改功能,當(dāng)你翻回這里看到自己當(dāng)初寫(xiě)下的那一片迷之縮進(jìn),你能保證自己將迅速看懂,而不是對(duì)著代碼重新捋一遍思路?)。
更多內(nèi)容請(qǐng)看下一篇文章RxJava詳解(三)
參考:
- RxJava Wiki
- Grokking RxJava, Part 1: The Basics
- NotRxJava
- When Not to Use RxJava
- 給 Android 開(kāi)發(fā)者的 RxJava 詳解
- Google Agera 從入門(mén)到放棄