手把手教你使用 RxJava 2.0(二)

本篇文章主要介紹線程調度器,通過對線程調度器的瞭解,方便我們更好的處理異步操作,在合適的場景選擇合適的線程。同時,結合上篇文章,我們就初步掌握了 RxJava 2.x的基本操作並可以應用在我們的項目中。在本篇文章的後半部分,會具體展示RxJava 2.x的使用。

Scheduler簡介

在不指定線程的情況下, RxJava 遵循的是線程不變的原則,即:在哪個線程調用 subscribe(),就在哪個線程生產事件;在哪個線程生產事件,就在哪個線程消費事件。如果需要切換線程,就需要用到 Scheduler (調度器)。
在RxJava 中,Scheduler,相當於線程控制器,RxJava 通過它來指定每一段代碼應該運行在什麼樣的線程。RxJava 已經內置了幾個 Scheduler ,它們已經適合大多數的使用場景。

Scheduler 的 API

● Schedulers.immediate(): 直接在當前線程運行,相當於不指定線程。這是默認的 Scheduler。

●Schedulers.newThread(): 總是啓用新線程,並在新線程執行操作。

●Schedulers.io(): I/O 操作(讀寫文件、讀寫數據庫、網絡信息交互等)所使用的 Scheduler。行爲模式和 newThread() 差不多,區別在於 io() 的內部實現是用一個無數量上限的線程池,可以重用空閒的線程,因此多數情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中,可以避免創建不必要的線程。

●Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小爲 CPU 核數。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。

● Android 還有一個專用的AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行。

有了這幾個 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 兩個方法來對線程進行控制了。subscribeOn(): 指定Observable(被觀察者)所在的線程,或者叫做事件產生的線程。 observeOn(): 指定 Observer(觀察者)所運行在的線程,或者叫做事件消費的線程。

下面用代碼展示下線程調度的使用:

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                Log.d("所在的線程:",Thread.currentThread().getName());
                Log.d("發送的數據:", 1+"");
                e.onNext(1);
            }
        }).subscribeOn(Schedulers.io()) 
                .observeOn(AndroidSchedulers.mainThread()) /
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d("所在的線程:",Thread.currentThread().getName());
                        Log.d("接收到的數據:", "integer:" + integer);
                    }
                });

01-19 10:06:38.275 27734-27783/? D/所在的線程:: RxCachedThreadScheduler-1
01-19 10:06:38.275 27734-27783/? D/發送的數據:: 1
01-19 10:06:38.285 27734-27734/? D/所在的線程:: main
01-19 10:06:38.285 27734-27734/? D/接收到的數據:: integer:1

可以看到,Observable(被觀察者)發送事件的線程的確改變了, 是在一個叫 RxCachedThreadScheduler-1的線程中發送的事件, 而Observer(觀察者)仍然在主線程中接收事件。由此我們實現了線程調度的操作,可以在此基礎上盡情的進行異步操作。

下面來介紹一個具體的使用場景。

RxJava 2.x 網絡請求使用

Android中有多種網絡請求庫, Retrofit便是其中的佼佼者,它的優勢之一便是它支持RxJava的方式來調用。我們便以Retrofit進行網絡請求,RxJava進行異步處理,兩者結合來講解RxJava在網絡請求中的具體使用。

本例中 我們使用聚合數據中的全國天氣數據,獲得城市信息。
接口url:http://v.juhe.cn/weather/citys?key=…. 其中key是你申請時聚合數據給你的密鑰。
具體請求的返回數據形式如下:

下面以上述數據簡單講解一下Retrofit的基本用法。

要使用Retrofit,先在Gradle中添加配置:

//Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
//Gson converter
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//Okhttp
    compile 'com.squareup.okhttp3:okhttp:3.5.0'
//RxJava adapter
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

定義Api接口:

public interface Api {
    @GET("citys")
    Observable<AllCity> getAllCity(@Query("key") String key);
}

創建一個Retrofit客戶端:

private static Retrofit create() {
        OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
        builder.readTimeout(10, TimeUnit.SECONDS);
        builder.connectTimeout(9, TimeUnit.SECONDS);

        return new Retrofit.Builder().baseUrl(baseUrl)
                .client(builder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

接下來就可以進行網絡請求:

Retrofit retrofit = create();
        Api api = retrofit.create(Api.class);
        Observable<AllCity> observable = api.getAllCity(appkey);
        observable.subscribeOn(Schedulers.io())
                .flatMap(new Function<AllCity, ObservableSource<City>>() {
                    @Override
                    public ObservableSource<City> apply(AllCity city) throws Exception {
                        ArrayList<City> result = city.getResult();
                        return Observable.fromIterable(result);
                    }
                })
                .filter(new Predicate<City>() {
                    @Override
                    public boolean test(City city) throws Exception {
                        String id = city.getId();
                        if(Integer.parseInt(id)<5){
                            return true;
                        }
                        return false;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<City>() {
                    @Override
                    public void accept(City city) throws Exception {
                        System.out.println(city);
                    }
                });

01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id=’1’, province=’北京’, city=’北京’, district=’北京’}
01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id=’2’, province=’北京’, city=’北京’, district=’海淀’}
01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id=’3’, province=’北京’, city=’北京’, district=’朝陽’}
01-19 13:28:56.952 13218-13218/com.lvr.rxjavalearning I/System.out: City{id=’4’, province=’北京’, city=’北京’, district=’順義’}

調用Api接口方法,返回一個Observable(被觀察者)對象,然後當subscribe()訂閱後,就可以在IO線程中執行網絡 請求操作,然後進行轉換過濾,最終Observer(觀察者)對象在UI線程中獲得城市id在1-4之間的城市信息。
其中請求返回的數據是json形式,AllCity類包含所有的返回數據,具體代碼如下:

public class AllCity {
    private String error_code;
    private String reason;
    private String resultcode;
   private ArrayList<City> result;
//省略getter,setter方法
}

ArrayList集合中封裝了所有城市的信息,City類包含城市詳細信息,具體代碼如下:

public class City {

    /**
     * id : 1
     * province : 北京
     * city : 北京
     * district : 北京
     */

    private String id;
    private String province;
    private String city;
    private String district;
    //省略getter,setter,toString方法
}

本例中,我們假設Observer(觀察者)需要id號在1-4之間的城市信息,我們就可以先使用flatMap()操作符先將封裝所有信息的AllCity中提取出城市信息集合,然後轉換成一個新的Observable(被觀察者)進行傳遞,然後使用filter()進行過濾,過濾出符合要求的城市信息,最終傳遞給Observer(觀察者),讓其在UI線程接收數據,然後更新UI。整個過程完成了網絡請求,同時進行異步操作,防止阻塞UI線程。
以上僅僅以實例介紹RxJava的基礎使用,RxJava的功能遠不止於此。不過掌握了以上的技能,我們已經可以在我們的項目中應用RxJava進行異步操作了。關於一些RxJava中的細節及其他相關技術還需要慢慢積累。

下面我們另一個重要的概念Disposable。當Observer(觀察者)與Observable(被觀察者)通過subscribe()建立連接後,事件可以進行傳遞。當發生一些其他情況,不得不斷開兩者之間的連接時,該怎麼操作?這個時候就該Disposable上場了。

Disposable簡介及使用

Disposable簡介

Disposable, 這個單詞的字面意思是一次性用品,用完即可丟棄的。在RxJava中,用它來切斷Observer(觀察者)與Observable(被觀察者)之間的連接,當調用它的dispose()方法時, 它就會將Observer(觀察者)與Observable(被觀察者)之間的連接切斷, 從而導致Observer(觀察者)收不到事件。
下面我們就該考慮如何來獲得Disposable對象?
Disposable的作用是切斷連接,確切地講是將Observer(觀察者)切斷,不再接收來自被觀察者的事件,而被觀察者的事件卻仍在繼續執行。
因此Disposable的對象通過觀察者獲得,具體分爲兩種方式。

Disposable對象的獲得

1.Observer接口

Observer<String> observer = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(String s) {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        };

通過創建Observer接口,當訂閱後,建立與Observable的聯繫,onSubscribe(Disposable d)中便可以獲得Disposable對象。

2.Consumer等其他函數式接口

Disposable disposable = Observable.just("你好").subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {

            }
        });

當subscribe()後直接返回一個Disposable 對象
獲得了Disposable對象後,我們便可以調用dispose()方法,在恰當的時機,斷開連接,停止接收Observable(被觀察者)發送的事件。

注意:當切斷被觀察者與觀察者之間的聯繫,Observable(被觀察者)的事件卻仍在繼續執行。

另外,補充一下onNext()、onComplete()和onError()事件的發送規則。

具體規則:

Observable(被觀察者)可以發送無限個onNext, Observer(觀察者)也可以接收無限個onNext.

當Observable(被觀察者)發送了一個onComplete後, Observable(被觀察者)中onComplete之後的事件將會繼續發送, 而Observer(觀察者)收到onComplete事件之後將不再繼續接收事件.

當Observable(被觀察者)發送了一個onError後, Observable(被觀察者)中onError之後的事件將繼續發送, 而Observer(觀察者)收到onError事件之後將不再繼續接收事件.

Observable(被觀察者)可以不發送onComplete或onError.

最爲關鍵的是onComplete和onError必須唯一併且互斥, 即不能發多個onComplete, 也不能發多個onError, 也不能先發一個onComplete, 然後再發一個onError, 反之亦然

注: 關於onComplete和onError唯一併且互斥這一點, 是需要自行在代碼中進行控制, 如果你的代碼邏輯中違背了這個規則, 並不一定會導致程序崩潰. 比如發送多個onComplete是可以正常運行的, 依然是收到第一個onComplete就不再接收了, 但若是發送多個onError, 則收到第二個onError事件會導致程序會崩潰。

以上就是本篇文章的全部內容,結合上一篇文章,已經可以靈活使用RxJava了。在下篇文章中,將會介紹RxJava中新增加的內容:Flowable及backpressure。

發佈了44 篇原創文章 · 獲贊 54 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章