安卓异步框架RxJava


一、关于RxJava

1.1 RxJava 到底是什么?

一种帮助你做异步的框架. 类似于 AsyncTask. 但其灵活性和扩展性远远强于前者. 从能力上讲, 如果说 AsycnTask 是 DOS 操作系统, RxJava 是 Window 操作系统。

1.2 RxJava 好在哪?

换句话说,『同样是做异步,为什么人们用它,而不用现成的 AsyncTask / Handler / XXX / … ?
一个词:简洁

异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的AsyncTask和Handler,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。

1.3 RxJava示例

在这里插入图片描述

int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
    @Override
    public void call(Subscriber<? super Drawable> subscriber) {
        Drawable drawable = getTheme().getDrawable(drawableRes));
        subscriber.onNext(drawable);
        subscriber.onCompleted();
    }
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Observer<Drawable>() {
    @Override
    public void onNext(Drawable drawable) {
        imageView.setImageDrawable(drawable);
    }

    @Override
    public void onCompleted() {
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
    }
});

1.4 RXjava原理浅析

但是rxjava和传统的观察者模式不完全相同,传统的观察者模式是涉及到两个对象------观察者(Observer)和被观察者Observable 。观察者通过将被观察 的对象加到自己的观察队列中,当被观察者发生改变时,就会通知观察者东西已经改变。

而RXJava中涉及到其他概念:

Observable (可观察者,即被观察者)、
Observer (观察者)、
subscribe (订阅)事件。
onNext()、onCompleted() 和 onError()。

Observable 和Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer数据刷新。

注意:重点来了
与传统观察者模式不同, RxJava 的事件回调方法除了普通事件 onNext() (相当于 onClick() / onEvent())之外,还定义了两个特殊的事件:onCompleted()和 onError()。

onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext() 发出时,需要触发 onCompleted() 方法作为标志。
onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。

注意:在一个正确运行的事件序列中, onCompleted() 和 onError() 有且只有一个,也就是说onCompleted() 和 onError() 二者也是互斥的。在响应的队列中只能调用一个。

1.5 RXjava事件处理模型图

在这里插入图片描述


二、RxJava基本实现

基于以上的概念, RxJava 的基本实现主要有三点:

2.1 创建Observer

Observer 即观察者,它决定事件触发的时候将有怎样的行为。 RxJava 中的Observer接口的实现方式:

 Observer observer = new Observer(){
   @Override
   public void onNext(String s){
       Log.d(tag,"Item: "+s);
   }
   @Override
   public void onCompleted(){
       Log.d(tag,"Completed!");
   }
   @Override
   public void onError(Throwable e){
       Log.d(tag,"Error!");
   }
 };

除了Observer接口之外,RxJava 还内置了一个实现了Observer的抽象类:Subscriber。Subscriber对Observer接口进行了一些扩展,但他们的基本使用方式是完全一样的:

Subscriber subscriber = newSubscriber(){
   @Override
   public void onNext(String s){
       Log.d(tag,"Item: "+s);}
   @Override
   public void onCompleted(){
       Log.d(tag,"Completed!");
   }
   @Override
   public void onError(Throwablee){
       Log.d(tag,"Error!");
   }
 };

不仅基本使用方式一样,实质上,在 RxJava 的 subscribe 过程中,Observer也总是会先被转换成一个Subscriber再使用。所以如果你只想使用基本功能,选择Observer和Subscriber是完全一样的。它们的区别对于使用者来说主要有两点:

  • onStart(): 这是Subscriber增加的方法。它会在 subscribe 刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行),onStart()就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用doOnSubscribe()方法.

  • unsubscribe(): 这是Subscriber所实现的另一个接口Subscription的方法,用于取消订阅。在这个方法被调用后,Subscriber将不再接收事件。一般在这个方法调用前,可以使用isUnsubscribed()先判断一下状态。unsubscribe()这个方法很重要,因为在subscribe()之后,Observable会持有Subscriber的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如onPause()onStop()等方法中)调用unsubscribe()来解除引用关系,以避免内存泄露的发生。

2.2 创建Observable

Observable 即被观察者,它决定什么时候触发事件以及触发怎样的事件。 RxJava 使用create()方法来创建一个 Observable ,并为它定义事件触发规则:

 Observable observable = Observable.create(new Observable.OnSubscribe(){
   @Override
   public void call(Subscriber subscriber){
       subscriber.onNext("Hello");
       subscriber.onNext("Hi");
       subscriber.onNext("Aloha");
       subscriber.onCompleted();
   }
 });

可以看到,这里传入了一个OnSubscribe对象作为参数。OnSubscribe会被存储在返回的Observable对象中,它的作用相当于一个计划表,当Observable被订阅的时候,OnSubscribe的call()方法会自动被调用,事件序列就会依照设定依次触发(对于上面的代码,就是观察者Subscriber将会被调用三次onNext()和一次onCompleted())。这样,由被观察者调用了观察者的回调方法,就实现了由被观察者向观察者的事件传递,即观察者模式。

这个例子很简单:事件的内容是字符串,而不是一些复杂的对象;事件的内容是已经定好了的,而不像有的观察者模式一样是待确定的(例如网络请求的结果在请求返回之前是未知的);所有事件在一瞬间被全部发送出去,而不是夹杂一些确定或不确定的时间间隔或者经过某种触发器来触发的。总之,这个例子看起来毫无实用价值。但这是为了便于说明,实质上只要你想,各种各样的事件发送规则你都可以自己来写。至于具体怎么做,后面都会讲到,但现在不行。只有把基础原理先说明白了,上层的运用才能更容易说清楚。

create()方法是 RxJava 最基本的创造事件序列的方法。基于这个方法, RxJava 还提供了一些方法用来快捷创建事件队列,例如:just(T…): 将传入的参数依次发送出来。

 Observable observable = Observable.just("Hello","Hi","Aloha");
 // 将会依次调用:
 // onNext("Hello");
 // onNext("Hi");
 // onNext("Aloha");
 // onCompleted();

from(T[ ]) / from(Iterable): 将传入的数组或Iterable拆分成具体对象后,依次发送出来。

 String[] words = {"Hello","Hi","Aloha"};
 Observable observable = Observable.from(words);
 // 将会依次调用:
 // onNext("Hello");
 // onNext("Hi");
 // onNext("Aloha");
 // onCompleted();

上面just(T…)的例子和from(T[ ])的例子,都和之前的create(OnSubscribe)的例子是等价的。

2.3 Subscribe(订阅)

创建了Observable和Observer之后,再用subscribe()方法将它们联结起来,整条链子就可以工作了。代码形式很简单:

 observable.subscribe(observer);
 // 或者:
 observable.subscribe(subscriber);

subscribe()这个方法有点怪:它看起来是『observalbe订阅了observer/subscriber』而不是『observer/subscriber订阅了observalbe』,这看起来就像『杂志订阅了读者』一样颠倒了对象关系。这让人读起来有点别扭,不过如果把 API 设计成observer.subscribe(observable)/subscriber.subscribe(observable),虽然更加符合思维逻辑,但对流式 API 的设计就造成影响了,比较起来明显是得不偿失的。

Observable.subscribe(Subscriber)的内部实现是这样的(仅核心代码):

 public Subscription subscribe(Subscriber subscriber){
     subscriber.onStart();
     onSubscribe.call(subscriber);
     return subscriber;
 }

可以看到,subscriber()做了3件事:

  • 调用Subscriber.onStart()。这个方法在前面已经介绍过,是一个可选的准备方法。
  • 调用Observable中的OnSubscribe.call(Subscriber)。在这里,事件发送的逻辑开始运行。从这也可以看出,在 RxJava 中,Observable并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当subscribe()方法执行的时候。
  • 将传入的Subscriber作为Subscription返回。这是为了方便unsubscribe().

整个过程中对象间的关系如下图:

在这里插入图片描述

动图:
在这里插入图片描述


三、Scheduler(线程调度器)

RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。而如果要实现线程的调度,就需要scheduler(线程调度器)。

RxJava 已经内置了几个 Scheduler ,它们已经适合大多数的使用场景:

  • 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 主线程运行。

Sceeduler默认给我们提供了subscribeOn() 和 observeOn() 两个方法来对线程进行控制 。

举个例子:

Observable.just(1, 2, 3, 4)
                .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer number) {
                    }
                });

上面这段代码中,由于 subscribeOn(Schedulers.io()) 的指定,被创建的事件的内容 1、2、3、4 将会在 IO 线程发出;而由于observeOn(AndroidScheculers.mainThread()) 的指定,因此 subscriber 数字的打印将发生在主线程 。事实上,这种在 subscribe() 之前写上两句subscribeOn(Scheduler.io())observeOn(AndroidSchedulers.mainThread())的使用方式非常常见,它适用于多数的 『后台线程取数据,主线程显示』的程序策略。

int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
    @Override
    public void call(Subscriber<? super Drawable> subscriber) {
        Drawable drawable = getTheme().getDrawable(drawableRes));
        subscriber.onNext(drawable);
        subscriber.onCompleted();
    }
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Observer<Drawable>() {
    @Override
    public void onNext(Drawable drawable) {
        imageView.setImageDrawable(drawable);
    }

    @Override
    public void onCompleted() {
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
    }
});

这样,加载图片发生在UI线程,而设置显示放到子线程出来,这样就不会出现卡顿。

四、变换

RxJava 提供了对事件序列进行变换的支持,这是它的核心功能之一。所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。

来看一个例子:

Observable.just("images/logo.png") // 输入类型 String
    .map(new Func1<String, Bitmap>() {
        @Override
        public Bitmap call(String filePath) { // 参数类型 String
            return getBitmapFromPath(filePath); // 返回类型 Bitmap
        }
    })
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) { // 参数类型 Bitmap
            showBitmap(bitmap);
        }
    });

这里出现了一个 Func1 的类。它和 Action1 非常相似,也是 RxJava 的一个接口,用于包装含有一个参数的方法。 Func1 和 Action的区别在于, Func1 包装的是有返回值的方法。FuncX 和ActionX 的区别在 FuncX 包装的是有返回值的方法。

通过上面的代码我们看到:map() 方法将参数中的 String 对象转换成一个 Bitmap 对象后返回,而在经过 map() 方法后,事件的参数类型也由 String转为了 Bitmap。这就是最长久的转换。

map(): 事件对象的直接变换示意图:

在这里插入图片描述

flatMap(): 这是一个很有用但非常难理解的变换

首先假设这么一种需求:假设有一个数据结构『学生』,现在需要打印出一组学生的名字。实现方式很简单:

Student[] students = ...;
Subscriber<Student> subscriber = new Subscriber<Student>() {
    @Override
    public void onNext(Student student) {
        List<Course> courses = student.getCourses();
        for (int i = 0; i < courses.size(); i++) {
            Course course = courses.get(i);
            Log.d(tag, course.getName());
        }
    }
    ...
};
Observable.from(students)
    .subscribe(subscriber);

很简单。那么再假设:如果要打印出每个学生所需要修的所有课程的名称呢?(需求的区别在于,每个学生只有一个名字,但却有多个课程。)首先可以这样实现:

在这里插入图片描述
依然很简单。那么如果我不想在Subscriber中使用 for 循环,而是希望Subscriber中直接传入单个的Course对象呢(这对于代码复用很重要)?用map()显然是不行的,因为map()是一对一的转化,而我现在的要求是一对多的转化。那怎么才能把一个 Student 转化成多个 Course 呢?

这个时候,就需要用flatMap()了:

在这里插入图片描述
从上面的代码可以看出,flatMap()和map()有一个相同点:它也是把传入的参数转化之后返回另一个对象。但需要注意,和map()不同的是,flatMap()中返回的是个Observable对象,并且这个Observable对象并不是被直接发送到了Subscriber的回调方法中。flatMap()的原理是这样的:

  1. 使用传入的事件对象创建一个Observable对象;
  2. 并不发送这个Observable, 而是将它激活,于是它开始发送事件;
  3. 每一个创建出来的Observable发送的事件,都被汇入同一个Observable,而这个Observable负责将这些事件统一交给Subscriber的回调方法。

这三个步骤,把事件拆成了两级,通过一组新创建的Observable将初始的对象『铺平』之后通过统一路径分发了下去。而这个『铺平』就是flatMap()所谓的 flat。

flatmap运行原理图:

在这里插入图片描述

变换的原理:lift()
这些变换虽然功能各有不同,但实质上都是针对事件序列的处理和再发送。而在 RxJava 的内部,它们是基于同一个基础的变换方法:lift(Operator)。首先看一下 lift() 的内部实现(仅核心代码):

// 注意:这不是 lift() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);
        }
    });
}


五、RxJava 的适用场景和使用方式

  1. 与 Retrofit 的结合
  2. RxBinding
  3. 各种异步操作
  4. RxBus

参考链接:
RxJava 详解
RxJava之create方法 以及ObservableOnSubscribe和ObservableEmitter
RxJava——操作符篇
RxJava 解除订阅---------Disposable.dispose()方法
RxJava 的 Disposable


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值