RxJava(一)概述与基本使用

RxJava学习系列:

RxJava(一)概述与基本使用

[RxJava(二)创建操作符]

RxJava是近年来非常火热且复杂的Android框架,本文基于RxJava 1.2.9来对其进行分析。

使用RxJava需要在build.gradle中修改如下代码:

dependencies {
...
compile 'io.reactivex:rxjava:1.2.9' //需要添加的代码
compile 'io.reactivex:rxandroid:1.2.1' //rxandroid的依赖,基于rxjava的扩展库
}

一、RxJava概述

RxJava是函数响应式编程在Java语言上的实现,在了解RxJava之前我们先来简单学习下什么是函数响应式编程。

函数响应式编程

函数响应式编程是函数式编程和响应式编程这两种编程范式的结合。

函数式编程(Functional Programming)是通过函数的调用与组合来处理数据,获取计算结果的一种编程范式。

在函数式编程中,函数是"第一等公民",即与其他数据类型地位相同:

可以赋值给其他变量,因为在在函数式编程中,只用"表达式",即每一步都是一个运算过程,都有返回值,函数也必须都有返回值

也可以作为参数传递给其他函数,比如闭包作为参数传递。

函数式编程的优点:

简洁易懂,通过函数的链式调用使代码可读性更强

只依赖输入的特性,每一个函数都可以看做一个独立的单元,使代码更易管理

由于函数不修改变量,所以不需要考虑死锁的问题,易于并发编程

响应式编程(Reactive Programming)是一种面向数据流和变化传播的编程范式。

在响应式编程中,任何的事件都看做是数据流的形式。上游发射数据流,下游监听数据流,在传递的过程中,对数据流进行过滤,转变,合并,去重等处理,当下游接受到数据流时,对其做出响应。

在界面显示中,将要显示的数据源(从网络请求,数据库查询中得到)以数据流的形式,通过一系列的流转过程(对数据进行处理,后台线程发送到UI线程),交给界面,界面在对数据做出相应的响应。在界面的交互中,也是如此,将用户输入的点击,触摸等事件以数据流的形式经过层层传递交给对应的窗口,在到对应的控件,控件监听到相应的事件后,响应用户的行为。

函数响应式编程(Functional reactive programming)是通过函数块(map,reduce,filter)来处理异步数据流的一种编程范式。

在RxJava中,函数响应式编程通过设置一个可观察对象(Observable)和观察者(Observer/Subscriber),Subscriber监听Observable的事件,Observable以数据流的形式发送事件,再通过一系列函数的链式调用(map/flatmap/filter等)对数据流进行转变和通过线程调度器(Scheduler)对数据流进行并发处理,最后由Subscriber接受到事件后对事件作出响应。

RxJava的定义

了解了函数响应式编程后,我们就能理解RxJava的定义了。

RxJava是响应式扩展的JVM实现:一个使用可观察序列组成异步的,基于事件的程序的库。

总的来说,RxJava就是一个实现异步操作的库,相比于AsyncTask/Handler等异步操作的机制来说,RxJava的优点在于其使用了函数式的编程范式,使用函数的链式调用对数据流的发送,流转,接受,响应进行处理,使得代码异常简洁明了,再加上JDK8中支持的lambda表达式可以使代码变的更为简化。

二、观察者模式

RxJava的实现使用了观察者模式,我们就来讲一下观察者模式。

什么是观察者模式

观察者模式的机制是存在一个被观察者和观察者,观察者对被观察者的某种特征进行监听,当这种特征发生变化时,观察者立刻做出反应。例如,在监狱里,狱警是观察者,犯人是被观察者,狱警对犯人的打开牢门的行为进行了监听,当犯人打开牢门时,狱警需要立刻冲上去把犯人制服。这就是观察者模式,狱警需要时刻紧盯犯人的行为,观察其是否有打开牢门的行为。

编程中的观察者模式

程序中的观察者模式则有略微不同,观察者不需要时刻去紧盯被观察者,而是去订阅(Subscribe)或注册(Register)观察者感兴趣的行为,当被观察者发生这种行为时,让被观察者去通知他(在上例中就是狱警告诉犯人,你要开牢门的时候要提醒我)。

通常的模式是这样的:Observable内部有一个成员变量mObserver,通过订阅函数setObserver来设置订阅者,建立订阅关系,同时在被观察的事件发生时调用通知函数notifyObserver来通知mObserver去调用它的响应函数。

点击事件中的观察者模式

Android中有很多观察者的例子,比如注册监听事件就是一个很典型的例子。被观察者View调用setOnClickListener来给自己设置一个观察者OnClickListener,约定好OnClickListener订阅了View的点击事件。当View被点击时,会调用performClick方法去通知OnClickListener,调用OnClickListener.onClick方法,即OnClickListener对点击事件作出响应。

View&OnClickListener.png

三、RxJava的基本使用

RxJava中基本的实现为创建观察者,创建被观察者,建立订阅关系

第一步:创建Subscriber/Observer

首先是创建一个观察者Subscriber/Observer,其中Observer是一个接口,其中包含onNext(T t),onCompleted(),onError(Throwable e),三个抽象方法,而Subscriber是一个抽象类,实现了Observer接口,上述三个方法的实现由其子类来实现,在使用上两者区别不大,在Observable.subscribe方法中如果传入Observer,会将其封装成Subscriber来进行订阅的。Subscriber提供了更多的功能,在之后再进行讲解。

创建Subscriber:

Subscriber subscriber = new Subscriber<>() {
@Override
public void onNext(String s) {
Log.d("TAG", "onNext: " + s);
}
@Override
public void onCompleted() {
Log.d("TAG", "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.d("TAG", "onError");
}
};

RxJava中将事件看做是一个队列,关于这三个事件回调方法作用如下:

onNext(T t):不断的处理下一个事件,直至队尾

onCompleted():当不在有新的事件时,调用这个方法作为结束标志,这个方法会在最后一个onNext调用之后调用

onError(Throwable e):当事件队列出现异常时,会触发这个方法,并终止事件队列,不在处理新的事件。

onError和onCompleted在一个处理流里面只会调用一个。

第二步:创建Observable

调用Observable.create(OnSubcribe)来创建一个Observable,OnSubscirbe继承了Action1接口,Action1接口中只包含一个方法void call(T t)。在RxJava源码中有很多ActionX的接口,X表示接口的泛型个数和call方法的参数个数,call中每个参数与泛型类型一一对应。

我们先来看OnSubscribe的代码:

/**
* Invoked when Observable.subscribe is called.
* @param the output value type
*/
public interface OnSubscribe extends Action1> {
// cover for generics insanity
}

从注释可以看出在建立订阅关系Observable.subscribe调用后会去调用OnSubscribe.call方法,所以call中就应该实现Subscriber对事件作出的响应的处理逻辑。

创建Observable:

Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscribe super String> subscriber) {
//subcricber对事件的响应逻辑
subscriber.onNext("first");
subscriber.onNext("second");
subscriber.onNext("third");
subscriber.onCompleted();
}
} );

除了通过Observable.create来创建Observable还可以通过just(T... t),from(T[] t),from(Iterable extends T> iterable)来进行创建Observable`

just传入的是多个事件参数:

Observable observable = Observable.just("first", "second", "third");
from传入的是一个事件数组:
String[] s = {"first", "second", "third"};
Observable observable = Observable.from(s);
上述两种方式都和第一种方式没有区别,just和from都在其内部对事件回调函数进行了调用
第三步:建立订阅关系
通过Observable.subscribe(subscriber)来进行订阅关系的建立
observable.subscribe(subscirber);
subscribe返回的不是Observable,是Subscription接口,后序系列详解这个接口
除了这种方式,RxJava还提供了订阅不完整定义的事件回调函数,在subscribe方法中直接传入事件回调函数:
Subscription subscribe(final Action1 super T> onNext)
Subscription subscribe(final Action1 super T> onNext, final Action1 onError)
Subscription subscribe(final Action1 super T> onNext, final Action1 onError, final Action0 onCompleted) {

前面已经讲过了ActionX接口的含义,这里subscribe的三个重载就是可以不用麻烦的创建Subscriber,而是直接去实现回调方法就行了。三个回调方法与重载参数的顺序是确定的,看上述方法参数的形参名就知道了。

以第三个方法为例,实现跟之前创建Subscriber一样的效果:

observable.subscribe(new Action1() {
@Override
public void call(String s) {
System.out.println("onNext: " + s);
}
}, new Action1() {
@Override
public void call(Throwable throwable) {
System.out.println("onError");
}
}, new Action0() {
@Override
public void call() {
System.out.println("onCompleted");
}
});

可以根据方法的重载(第一种和第二种情况),不用将三个方法都实现,这样未实现的方法就默认为什么操作都不执行。

通过以上方式,我们就实现了对RxJava的基本使用,RxJava使用函数式编程的思想,支持链式调用,合起来代码如下:

Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
//subcricber对事件的响应逻辑
subscriber.onNext("first");
subscriber.onNext("second");
subscriber.onNext("third");
subscriber.onCompleted();
}
}).subscribe(new Subscriber() {
@Override
public void onNext(String s) {
System.out.println("onNext: " + s);
}
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.out.println("onError");
}
});
运行结果如下:
onNext: first
onNext: second
onNext: third
onCompleted

整个的调用流程如下:

Observable&&Subscriber.png

首先通过Observable.create(OnSubscribe)来创建一个Observable对象,(当然你也可以通过just或者from的方式来创建,这样就不需要OnSubscribe了,在内部会自动去调用Subscriber的事件回调方法)。然后调用Observable.subcribe(Subscriber)传入一个实现了Subscriber的实现类,这个实现类里面实现了onNext,onCompleted,onError三个方法。

在Observable.subcribe(Subscriber)的内部会去调用OnSubscribe.call()这个方法,而我们在创建Observable时传入的OnSubscribe的匿名内部类中实现了call(Subscriber super String> subscriber)方法,去调用传入进来的subscriber的事件回调的三个函数,来对事件作出响应。

由此可以发现,RxJava中的观察者模式与一般的不同点在于:

RxJava中的事件是一个虚拟的事件,不是来源于外部注入的事件,而是是在创建被观察者的时候就已经将事件给注入进去了。

RxJava中被观察者是在与观察者建立订阅关系的同时,内部调用OnSubscribe的call方法去通知观察者对事件作出响应的。

四、RxJava的线程调度

前面已经讲述了RxJava的基本使用,但是你看了之后可能发现这个RxJava不是多此一举,整些花里胡哨的,直接调用几个方法去对这些事件进行处理不就行了,为什么还要整那些观察者,被观察者干嘛呢。刚开始我学习RxJava的时候也是这样觉得的,就觉得这个东西没什么用,还把自己搞的晕头转向的,但是后来学习了RxJava的异步处理之后才知道这样做的好处。其实前面也有将到RxJava的核心思想和优势,就是在于对数据流的转换和对异步操作的处理。所以这里我们就来先讲一下RxJava中的线程调度。

Scheduler

RxJava中提供了一个Scheduler——线程调度器的机制,其作用是指定RxJava中的每个过程在什么线程中运行,所以现在你可能就明白了一个很简单的事件响应为什么要分成多个过程来处理了,其原因就是为了利于线程的调度。

在Schedulers中定义了几个常用的Scheduler(几个静态方法的返回值):

Schedulers.immediate():默认的Scheduler,直接在当前线程运行,相当于不指定线程

Schedulers.newThread():启动一个新线程,在新线程中执行任务

Scheduler.io():用于IO异步阻塞操作的Scheduler,内部是用无上限的线程池实现的,可以重用空闲的线程,效率比newThread要高。不要把计算操作放在这个Scheduler中。

Scheduler.computation():用于事件循环,回调以及其他计算工作的Scheduler,内部是一个固定大小为CPU核数的线程池,不能把IO阻塞操作放在这个线程中

Schedulers.from(executor):使用指定的executor作为Scheduler

AndroidSchedulers.mainThread():RxAndroid中添加的Scheduler,表示在Android主线程中运行(RxAndroid是一个基于RxJava的扩展库,兼容了Android的一些特性)

现在我们就可以去设置每个过程中的Scheduler来指定每个过程的执行线程了。在RxJava中通过subcsribeOn()和observeOn()来设置执行线程,subscribeOn指定Observable.OnSubscribe调用call()方法时所在的线程,即事件产生的线程,observeOn()指定Subscriber事件回调的执行线程,即事件响应的线程。

那么现在我们就来写一个从IO线程读取图片的例子:

Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super Bitmap> subscriber) {
Bitmap bitmap = BitmapFactory.decodeFile(picPath);
subscriber.onNext(bitmap);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
Toast.makeText(RxJavaActivity.this, "图片加载成功", Toast.LENGTH_LONG).show();
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Bitmap bitmap) {
mIvLocalImage.setImageBitmap(bitmap);
}
});

上述代码就实现了在IO线程中去读取SD卡上的图片,然后在主线程中显示出来。将上述代码中完整的subscriber实现变为不完整的事件回调函数,并且使用lambda表达式,代码如下:

Observable.create(subscriber -> {
Bitmap bitmap = BitmapFactory.decodeFile(picPath);
subscriber.onNext(bitmap);
subscriber.onCompleted();
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
bitmap -> mIvLocalImage.setImageBitmap(bitmap),
e -> {},
() -> Toast.makeText(RxJavaActivity.this, "图片加载成功", Toast.LENGTH_LONG).show()
);

RxJava配合Lambda表达式实现异步操作,是不是使用起来特别方便简洁!

ps:以上就是关于RxJava的概述以及RxJava的基本使用了,关于RxJava的后续学习我会在后面的文章中进行讲解的,敬请关注!

[RxJava(二)创建操作符]: