手把手教你使用 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。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LRH0211/article/details/72841506
个人分类: RxJava学习
想对作者说点什么? 我来说一句

手把手教你使用Imagepro-plus

2010年04月23日 3.87MB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭