RxJava前世今生

需求

聚合數據API提供了搜索某個城市未來一周每天的天氣詳細信息,包含最低最高氣溫、風速、濕度等數據。我們的任務就是:獲取南京市未來一周的天氣情況,找出溫度最高的那一天的天氣詳細信息,把這條信息保存在數據庫中。

同步方法實現

我們只關注獲取數據、處理數據和保存數據的三個核心功能。
Model 和 API
簡單的描述一下Weather的數據模型

public class Weather implements Comparable<Weather> {

    private int max;//最高溫度
    private int min;//最低溫度
    private String wind;

    @Override
    public int compareTo(@NonNull Weather weather) {
        return Integer.compare(max,weather.max);
    }
}

下面是我們請求數據和存儲數據的API(同步),我們暫且忽略具體的實現

public interface Api {
    List<Weather> queryWeathers(String city);
    Uri save(Weather weather);
}

現在,開始編寫我們的業務邏輯代碼

public class WeathersHelper {
    Api api;
    public Uri saveTheMaxDay(String city){
        List<Weather> weathers = api.queryWeathers(city);
        Weather max = findMaxDay(weathers);
        Uri savedUri = api.save(max);
        return savedUri;
    }
 
    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

(@ο@) 哇~,這也太簡單太清晰了吧!來看看上面的代碼是多么的酷。主要的函數 saveTheMaxDay 只包含了 3個函數調用。使用參數來調用這些函數,并接收返回的參數。 并且等待每個函數執行并返回結果。如此簡單、如此有效。下面來看看這種簡單函數的其他優點。

  • 組合(Composition)
    可以看到我們的 saveTheMaxDay 由其他三個函數調用所組成的。我們通過函數來把一個大功能分割為每個容易理解的小功能。通過函數調用來組合使用這些小功能。使用和理解起來都相當簡單。
  • 異常傳遞
    另外一個使用函數的好處就是方便處理異常。每個函數都可以通過拋出異常來結束運行。該異常可以在拋出異常的函數里面處理,也可以在調用該函數的外面處理,所以我們無需每次都處理每個異常,我們可以在一個地方處理所有可能拋出的異常。
try{
    List<Weather> weathers = api.queryWeathers(city);
    Weather max = findMaxDay(weathers);
    Uri savedUri = api.save(max);
    return savedUri;
} catch (Exception e) {
    e.printStackTrace()
    return someDefaultValue;
}

這樣,我們就可以處理這三個函數中所拋出的任何異常了。如果沒有 try catch 語句,我們也可以把異常繼續傳遞下去。

異步回調方法實現

但是,現實世界中我們往往沒法等待。有些時候你沒法只使用阻塞調用。在 Android 中你需要處理各種異步操作。
就拿 Android 的 OnClickListener 接口來說吧, 如果你需要處理一個 View 的點擊事件,你必須提供一個 該 Listener 的實現來處理用戶的點擊事件。下面來看看如何處理異步調用。
現在我們把請求網絡放在異步操作中,使用回調獲取結果

public interface Api {
    interface WeathersQueryCallback {
        void onWeatherListReceived(List<Weather> weathers);
        void onError(Exception e);
    }
    void queryWeathers(String city, WeathersQueryCallback weathersQueryCallback);
    Uri save(Weather weather);
}

這樣我們查詢天氣的操作就變為異步的了, 通過 WeathersQueryCallback 回調接口來結束查詢的數據和處理異常情況。
我們的業務邏輯也需要跟著改變一下:

public class WeathersHelper {
 
    public interface MaxDayCallback {
        void onMaxDaySaved(Uri uri);
        void onQueryFailed(Exception e);
    }
    Api api;
    public void saveTheMaxDay(String city, MaxDayCallback maxDayCallback){
        api.queryWeathers(city, new Api.WeathersQueryCallback() {
            @Override
            public void onWeatherListReceived(List<Weather> weathers) {
                Weather max = findMaxDay(weathers);
                Uri savedUri = api.save(max);
                maxDayCallback.onMaxDaySaved(savedUri);
            }
 
            @Override
            public void onError(Exception e) {
                maxDayCallback.onQueryFailed(e);
            }
        });
    }
 
    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

這樣我們就沒法使用阻塞式函數調用了, 我們無法繼續使用阻塞調用來使用這些 API 了。所以,我們沒法讓 saveTheMaxDay 函數返回一個值了, 我們需要一個回調接口來異步的處理結果。
這里我們再進一步,使用兩個異步操作來實現我們的功能, 例如 我們還使用異步 IO 來寫文件。

public interface Api {
    interface WeathersQueryCallback {
        void onWeatherListReceived(List<Weather> weathers);
        void onError(Exception e);
    }
    interface SaveCallback{
        void onWeatherSaved(Uri uri);
        void onSaveFailed(Exception e);
    }
    void queryWeathers(String city, WeathersQueryCallback weathersQueryCallback);
    void save(Weather weather, SaveCallback saveCallback);
}

業務邏輯變為:

public class WeathersHelper {
 
    public interface MaxDayCallback {
        void onMaxDaySaved(Uri uri);
        void onQueryFailed(Exception e);
    }
 
    Api api;
 
    public void saveTheMaxDay(String city, MaxDayCallback maxDayCallback){
        api. queryWeathers(city, new Api.WeathersQueryCallback() {
            @Override
            public void onWeatherListReceived(List< Weather > weathers) {
                Weather max = findMaxDay(weathers);
                api.save(max, new Api.SaveCallback() {
                    @Override
                    public void onWeatherSaved(Uri uri) {
                        maxDayCallback.onMaxDaySaved(uri);
                    }
 
                    @Override
                    public void onStoreFailed(Exception e) {
                        maxDayCallback.onError(e);
                    }
                });
            }
 
            @Override
            public void onQueryFailed(Exception e) {
                maxDayCallback.onError(e);
            }
        });
    }
 
    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

現在再來看看我們的業務邏輯代碼,是不是和之前的阻塞式調用那么簡單、那么清晰? 當然不一樣了,上面的異步操作代碼看起來太恐怖了! 這里有太多的干擾代碼了,太多的匿名類了,但是不可否認,他們的業務邏輯其實是一樣的。查詢天氣的列表數據、找出最溫度最高的那一天。
組合功能也不見了! 現在你沒法像阻塞操作一樣來組合調用每個功能了。異步操作中,每次你都必須通過回調接口來手工的處理結果。
那么關于異常傳遞和處理呢? 沒了!異步代碼中的異常不會自動傳遞了,我們需要手工的重新傳遞出去。(onSaveFailed 和 onQueryFailed 就是干這事用的)
上面的代碼非常難懂也更難發現潛在的 BUG。

代碼優化

  • 泛型接口
    仔細的看看這些回調接口,你會發現一個通用的模式:
    1,這些接口都有一個函數來返回結果(onMaxDaySaved, onWeatherListReceived, onWeatherSaved)。
    2,這里還都有一個返回異常情況的函數(onError, onQueryFailed, onSaveFailed)。

所以我們可以使用一個泛型接口來替代這三個接口。 由于我們無法修改 API 調用的參數類型, 必須要創建一個包裝類來調用泛型接口。新的接口定義如下:

public interface Callback<T> {
    void onResult(T result);

    void onError(Exception e);
}

然后使用 ApiWrapper 來改變調用的參數。

public class ApiWrapper {
    Api api;

    public void queryWeathers(String city, final Callback<List<Weather>> weathersCallback) {
        api.queryWeathers(city, new Api.WeathersQueryCallback() {
            @Override
            public void onWeatherListReceived(List<Weather> weathers) {
                weathersCallback.onResult(weathers);
            }

            @Override
            public void onError(Exception e) {
                weathersCallback.onError(e);
            }
        });
    }

    public void save(Weather weather, final Callback<Uri> uriCallback) {
        api.save(weather, new Api.SaveCallback() {
            @Override
            public void onWeatherSaved(Uri uri) {
                uriCallback.onResult(uri);
            }

            @Override
            public void onSaveFailed(Exception e) {
                uriCallback.onError(e);
            }
        });
    }
}

上面的代碼使用同樣的邏輯在 Callback 接口中來處理結果和異常情況。
最后 WeathersHelper 的代碼如下:

public class WeatherHelper {

    ApiWrapper apiWrapper;

    public void saveTheMaxDay(String city, final Callback<Uri> maxDayCallback){
        apiWrapper.queryWeathers(city, new Callback<List<Weather>>() {

            @Override
            public void onResult(List<Weather> result) {
                Weather maxDay = findMaxDay(result);
                apiWrapper.save(maxDay,maxDayCallback);
            }

            @Override
            public void onError(Exception e) {
                maxDayCallback.onError(e);
            }
        });
    }

    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

由于使用了泛型回調接口,這里的maxDayCallback可以直接設置為函數 apiWrapper.save的參數, 所以 上面的代碼比前面的代碼要少一層匿名類。看起來簡單一點。

  • 分離參數和回調接口
    看看這些新的異步操作(queryWeathers, save和 saveTheMaxDay)。這些函數都有同樣的模式。使用一些參數來調用這些函數(city,weather),同時還有一個回調接口作為參數。甚至,所有的異步操作都帶有一些常規參數和一個額外的回調接口參數。如果我們把他們分離開會如何,讓每個異步操作只有一些常規參數而把返回一個臨時的對象來操作回調接口。下面來試試看看這種方式能否有效。
    如果我們返回一個臨時的對象作為異步操作的回調接口處理方式,我們需要先定義這個對象。由于對象遵守通用的行為(有一個回調接口參數),我們定義一個能用于所有操作的對象。 我們稱之為 AsyncJob。

注意: 我非常想把這個名字稱之為 AsyncTask。但是由于 Android 系統已經有個 AsyncTask 類了, 為了 避免混淆,所以就用 AsyncJob了。
該對象如下:

public abstract class AsyncJob<T> {
    public abstract void start(Callback<T> callback);
}

start 函數有個Callback 回調接口參數,并開始執行該操作。
ApiWrapper 修改為:

public class ApiWrapper {
    Api api;

    public AsyncJob<List<Weather>> queryWeathers(final String city) {
        return new AsyncJob<List<Weather>>() {
            @Override
            public void start(final Callback<List<Weather>> weathersCallback) {
                api.queryWeathers(city, new Api.WeathersQueryCallback() {
                    @Override
                    public void onWeatherListReceived(List<Weather> weathers) {
                        weathersCallback.onResult(weathers);
                    }

                    @Override
                    public void onError(Exception e) {
                        weathersCallback.onError(e);
                    }
                });
            }
        };
    }

    public AsyncJob<Uri> save(final Weather weather) {
        return new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> uriCallback) {
                api.save(weather, new Api.SaveCallback() {
                    @Override
                    public void onWeatherSaved(Uri uri) {
                        uriCallback.onResult(uri);
                    }

                    @Override
                    public void onSaveFailed(Exception e) {
                        uriCallback.onError(e);
                    }
                });
            }
        };
    }
}

目前看起來還不錯。現在可以使用 AsyncJob 來啟動每個操作了。 這些功能在 WeathersHelper 中:

public class WeatherHelper {
    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheMaxDay(final String city) {
        return new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> maxDayCallback) {
                apiWrapper.queryWeathers(city)
                        .start(new Callback<List<Weather>>() {
                            @Override
                            public void onResult(List<Weather> result) {
                                Weather maxDay = findMaxDay(result);
                                apiWrapper.save(maxDay)
                                        .start(new Callback<Uri>() {
                                            @Override
                                            public void onResult(Uri result) {
                                                maxDayCallback.onResult(result);
                                            }

                                            @Override
                                            public void onError(Exception e) {
                                                maxDayCallback.onError(e);
                                            }
                                        });
                            }

                            @Override
                            public void onError(Exception e) {

                            }
                        });
            }
        };
    }
    
    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

看起來比前面一個版本更加復雜啊,這樣有啥好處啊?
這里其實我們返回的是一個 AsyncJob 對象,該對象和客戶端代碼組合使用,這樣 在 Activity或者 Fragment 客戶端代碼中就可以操作這個返回的對象了。
代碼雖然目前看起來比較復雜,下面我們就來改進一下。

  • 分解
    下面是數據流圖:
         (async)                   (sync)               (async)
query ===========> List<Weather> -------------> Weather==========> Uri
       queryWeathers                findMax               save

為了讓代碼具有可讀性,我們把這個流程分解為每個操作。同時我們再進一步假設,如果一個操作是異步的,則每個調用該異步操作的函數也是異步的。例如:如果查詢天氣是個異步操作,則找到最最高溫度的那一天也是異步的。
因此,我們可以使用 AsyncJob 來把每個操作分解為一個非常小的函數里面去。

public class WeatherHelper {
    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheMaxDay(final String city) {
        //第一步,請求數據
        final AsyncJob<List<Weather>> weatherListAsyncJob = apiWrapper.queryWeathers(city);
        //第二步,處理數據
        final AsyncJob<Weather> maxDayAsyncJob = new AsyncJob<Weather>() {
            @Override
            public void start(final Callback<Weather> callback) {
                weatherListAsyncJob.start(new Callback<List<Weather>>() {
                    @Override
                    public void onResult(final List<Weather> result) {
                        Weather maxDay = findMaxDay(result);
                        callback.onResult(maxDay);
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
        //第三步,存儲數據
        AsyncJob<Uri> savedUriAsyncJob = new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> callback) {
                maxDayAsyncJob.start(new Callback<Weather>() {
                    @Override
                    public void onResult(Weather result) {
                        apiWrapper.save(result)
                                .start(new Callback<Uri>() {
                                    @Override
                                    public void onResult(Uri result) {
                                        callback.onResult(result);
                                    }

                                    @Override
                                    public void onError(Exception e) {
                                        callback.onError(e);
                                    }
                                });
                    }

                    @Override
                    public void onError(Exception e) {

                    }
                });
            }
        };

        return savedUriAsyncJob;
    }

    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

雖然代碼量多了,但是看起來更加清晰了。 嵌套的回調函數沒那么多層級了,異步操作的名字也更容易理解了(weatherListAsyncJob , maxDayAsyncJob , savedUriAsyncJob )。
看起來還不錯,但是還可以更好。

  • 簡單的映射
    先來看看我們創建 AsyncJob maxDayAsyncJob 的代碼:
AsyncJob<Weather> maxDayAsyncJob = new AsyncJob<Weather>() {
            @Override
            public void start(final Callback<Weather> callback) {
                weatherListAsyncJob.start(new Callback<List<Weather>>() {
                    @Override
                    public void onResult(final List<Weather> result) {
                        Weather maxDay = findMaxDay(result);
                        callback.onResult(maxDay);
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };

這 16 行代碼中,只有一行代碼是我們的業務邏輯代碼:findMaxDay(result)
其他的代碼只是為了啟動 AsyncJob 并接收結果和處理異常的干擾代碼。 但是這些代碼是通用的,我們可以把他們放到其他地方來讓我們更加專注業務邏輯代碼。
那么如何實現呢?需要做兩件事情:
1,轉換 AsyncJob 的結果
2,轉換的函數
但是由于 Java 的限制,無法把函數作為參數, 所以需要用一個接口(或者類)并在里面定義一個轉換函數:

public interface Function<T, R> {
    R call(T t);
}

該接口很簡單。 有兩個泛型類型定義, T 代表參數的類型; R 代表返回值的類型。
當我們把 AsyncJob 的結果轉換為其他類型的時候, 我們需要把一個結果值映射為另外一種類型,這個操作我們稱之為 map。 把該函數定義到 AsyncJob 類中比較方便,這樣就可以通過 this 來訪問 AsyncJob 對象了。

public abstract class AsyncJob<T> {
    public abstract void start(Callback<T> callback);

    /**
     * 簡單映射
     * @param func
     * @param <R> 輸入類型
     * @return 輸出類型
     */
    public <R> AsyncJob<R> map(final Function<T, R> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(final 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);
                    }
                });
            }
        };
    }
}

看起來不錯, 現在的 CatsHelper 如下:

public class WeatherHelper {
    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheMaxDay(final String city) {
        //第一步,請求數據
        final AsyncJob<List<Weather>> weatherListAsyncJob = apiWrapper.queryWeathers(city);
        //第二步,處理數據
        final AsyncJob<Weather> maxDayAsyncJob = weatherListAsyncJob.map(new Function<List<Weather>, Weather>() {
            @Override
            public Weather call(List<Weather> weathers) {
                return findMaxDay(weathers);
            }
        });
        //第三步,存儲數據
        AsyncJob<Uri> savedUriAsyncJob = new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> callback) {
                maxDayAsyncJob.start(new Callback<Weather>() {
                    @Override
                    public void onResult(Weather result) {
                        apiWrapper.save(result)
                                .start(new Callback<Uri>() {
                                    @Override
                                    public void onResult(Uri result) {
                                        callback.onResult(result);
                                    }

                                    @Override
                                    public void onError(Exception e) {
                                        callback.onError(e);
                                    }
                                });
                    }

                    @Override
                    public void onError(Exception e) {

                    }
                });
            }
        };

        return savedUriAsyncJob;
    }

    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

新的創建 AsyncJob maxDayAsyncJob 的代碼只有 6行,并且只有一層嵌套。

  • 高級映射
    但是 AsyncJob storedUriAsyncJob 看起來還是非常糟糕。 這里也能使用映射嗎? 下面就來試試吧!
public class WeatherHelper {
    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheMaxDay(final String city) {
        //第一步,請求數據
        final AsyncJob<List<Weather>> weatherListAsyncJob = apiWrapper.queryWeathers(city);
        //第二步,處理數據
        final AsyncJob<Weather> maxDayAsyncJob = weatherListAsyncJob.map(new Function<List<Weather>, Weather>() {
            @Override
            public Weather call(List<Weather> weathers) {
                return findMaxDay(weathers);
            }
        });
        //第三步,存儲數據
        AsyncJob<Uri> savedUriAsyncJob = maxDayAsyncJob.map(new Function<Weather, Uri>() {
            @Override
            public Uri call(Weather weather) {
                return apiWrapper.save(weather);
        //      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 將會導致無法編譯
        //      Incompatible types:
        //      Required: Uri
        //      Found: AsyncJob<Uri>   
            }
        });
        return savedUriAsyncJob;
    }

    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

哎。。。 看起來沒這么簡單啊, 下面修復返回的類型再試一次:

public class WeatherHelper {
    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheMaxDay(final String city) {
        //第一步,請求數據
        final AsyncJob<List<Weather>> weatherListAsyncJob = apiWrapper.queryWeathers(city);
        //第二步,處理數據
        final AsyncJob<Weather> maxDayAsyncJob = weatherListAsyncJob.map(new Function<List<Weather>, Weather>() {
            @Override
            public Weather call(List<Weather> weathers) {
                return findMaxDay(weathers);
            }
        });
        //第三步,存儲數據

        AsyncJob<AsyncJob<Uri>> savedUriAsyncJob = maxDayAsyncJob.map(new Function<Weather, AsyncJob<Uri>>() {
            @Override
            public AsyncJob<Uri> call(Weather weather) {
                return apiWrapper.save(weather);
            }
        });
        return savedUriAsyncJob;
        //^^^^^^^^^^^^^^^^^^^^^^^ 將會導致無法編譯
        //      Incompatible types:
        //      Required: AsyncJob<Uri>
        
    }

    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

這里我們只能拿到 AsyncJob<AsyncJob> 。 看來還需要更進一步。我們需要壓縮一層AsyncJob ,把兩個異步操作當做一個單一的異步操作來對待。

現在我們需要一個參數為 AsyncJob 的 map 轉換操作而不是 R。 該操作類似于 map, 但是該操作會把嵌套的 AsyncJob 壓縮為(flatten )一層 AsyncJob. 我們稱之為 flatMap, 實現如下:

public abstract class AsyncJob<T> {
    public abstract void start(Callback<T> callback);

    /**
     * 簡單映射
     * @param func
     * @param <R> 輸入類型
     * @return 輸出類型
     */
    public <R> AsyncJob<R> map(final Function<T, R> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(final 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);
                    }
                });
            }
        };
    }

    /**
     * 高級映射
     * @param func
     * @param <R>
     * @return
     */
    public <R> AsyncJob<R> flatMap(final Function<T, AsyncJob<R>> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(final 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);
                    }
                });
            }
        };
    }

}

看起來很多干擾代碼,但是還好這些代碼在客戶端代碼中并不會出現。 現在我們的 WeathersHelper 如下:

public class WeatherHelper {
    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheMaxDay(final String city) {
        //第一步,請求數據
        final AsyncJob<List<Weather>> weatherListAsyncJob = apiWrapper.queryWeathers(city);
        //第二步,處理數據
        final AsyncJob<Weather> maxDayAsyncJob = weatherListAsyncJob.map(new Function<List<Weather>, Weather>() {
            @Override
            public Weather call(List<Weather> weathers) {
                return findMaxDay(weathers);
            }
        });
        //第三步,存儲數據
        AsyncJob<Uri> savedUriAsyncJob = maxDayAsyncJob.flatMap(new Function<Weather, AsyncJob<Uri>>() {
            @Override
            public AsyncJob<Uri> call(Weather weather) {
                return apiWrapper.save(weather);
            }
        });
        
        return savedUriAsyncJob;
    }
    
    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

現在看起來簡單多了,最終的代碼 看起來是不是有點眼熟啊? 再仔細看看。還沒發現? 如果把匿名類修改為 Java 8 的 lambdas 表達式(邏輯是一樣的,只是讓代碼看起來更清晰點)就很容易發現了。

public class WeatherHelper {
    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheMaxDay(final String city) {

        AsyncJob<List<Weather>> weatherListAsyncJob = apiWrapper.queryWeathers(city);
        AsyncJob<Weather> maxDayAsyncJob = weatherListAsyncJob.map(weathers -> findMaxDay(weathers));
        AsyncJob<Uri> savedUriAsyncJob = maxDayAsyncJob.flatMap(weather -> apiWrapper.save(weather));

        return savedUriAsyncJob;
    }

    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

這樣看起來是不是就很清晰了。 這個代碼和剛剛開頭的阻塞式代碼是不是非常相似:

public class WeathersHelper {
    Api api;
    public Uri saveTheMaxDay(String city){
        List<Weather> weathers = api.queryWeathers(city);
        Weather max = findMaxDay(weathers);
        Uri savedUri = api.save(max);
        return savedUri;
    }
 
    private Weather findMaxDay(List<Weather> weathers) {
        return Collections.max(weathers);
    }
}

現在他們不僅邏輯是一樣的,語義上也是一樣的。 太棒了!
同時我們還可以使用組合操作,現在把兩個異步操作組合一起并返還另外一個異步操作。
異常處理也會傳遞到最終的回調接口中。

總結

下面來看看 RxJava 吧。
你沒必要把上面代碼應用到您的項目中去, 這些簡單的、線程不安全的代碼只是 RxJava 的一部分。只有一些名字上的不同:
AsyncJob 等同于 Observable, 不僅僅可以返回一個結果,還可以返回一系列的結果,當然也可能沒有結果返回。
Callback 等同于 Observer, 除了onNext(T t), onError(Throwable t)以外,還有一個onCompleted()函數,該函數在結束繼續返回結果的時候通知Observable 。
abstract void start(Callback callback) 和 Subscription subscribe(final Observer observer) 類似,返回一個Subscription ,如果你不再需要后面的結果了,可以取消該任務。除了 map 和 flatMap 以外, Observable 還有很多其他常見的轉換操作。
下一節,讓我們認識一下RxJava 中關鍵的類。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容