RxJava詳解(二)

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è),把它的圖片保存到本地。

ModelAPI

下面是貓的數(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中你需要處理各種異步操作。
就那AndroidOnClickListener接口來(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,storesaveTheCutestCat)。這些函數(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ù)為AsyncJobmap轉(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 8lambdas表達(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詳解(三)

參考:


更多精彩文章請(qǐng)見(jiàn):Github AndroidNote,歡迎Star

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,065評(píng)論 25 708
  • 年 少 離 鄉(xiāng) 老 白 發(fā) , 暗 夜 孤 酒 醉 天 涯 。 漂 泊 人 還 未 歸 家 , 總 無(wú) 奈 一 身...
    cb6a44a8559a閱讀 234評(píng)論 0 2
  • 堅(jiān)持是什么?這曾經(jīng)是個(gè)困擾我很久的問(wèn)題。三天打魚(yú) 兩天曬網(wǎng)曬網(wǎng),說(shuō)的就是從前的我。 為什么說(shuō)是從前的我呢?現(xiàn)在已經(jīng)...
    葡萄的小叮當(dāng)閱讀 262評(píng)論 2 5
  • 我是第一次參與這樣的群體寫(xiě)作,第一天的作業(yè)便是自我介紹,為了表達(dá)重視,毫不忌諱地告訴你,我已在腦海中勾畫(huà)多遍[自我...
    靜候師太閱讀 149評(píng)論 0 2