文章目录
关键词:
Observable 被观察者、Observer 观察者 、Subscribe 订阅方法、
Subscriber Observer的实现类 、Action 替代Subscriber
RxJava 是什么:异步
RxJava在Github 主页上的介绍是:
"a library for composing asynchronous and event-based programs using observable sequences for the Java VM"
翻译过来是:
一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库
RxJava 的好处是什么:简洁
Android创造的 AsyncTask 和 Handler ,其实都是为了让异步代码更加简洁。RxJava的优势也是简洁,但是其不同的是,随着程序逻辑变得越来越复杂,它依然保持简洁(逻辑简洁)
例子:
假设有这么一个需求,界面有一个自定义的视图imageCollectorView ,它的作用是显示多张图片,并能使用 addImage(Bitmap) 方法来任意增加显示的图片。现在需要程序将一个给出的目录数组 File[] floders 中每个目录下的png图片都加载出来并显示在自定义视图中。需要注意的是:由于读取图片的这一过程较为耗时,需要放在后台执行,而图片的显示则必须在UI线程执行。
常用的实现方法有多种,我们这里贴出其中一种:
普通方法实现
new Thread(){
@Override
public void run(){
for(File folder: floders){
File[] files = folder.listFile();
for(File file: files){
if(file.getName().endsWith(".png")){
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable(){
@Override
public void run(){
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}
}.start();
而如果使用RxJava,实现方式是这样的:
RxJava实现
Observable.from(folders) //将目录数组传入
.flatMap(new Funcl<File,Observable<File>>(){
@Override
public Observable<File> call(File file){
return Observable.from(file.listFiles());
}
})
.fileter(new Funcl<File,Boolean>(){
@Override
public Boolean call(File file){\
return file.getName().endsWith(".png");
}
})
.map(new Funcl<File,Bitmap>(){
@Override
public Bitmap call(File file){
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action<Bitmap>(){
@Override
public void call(Bitmap bitmap){
imageCollectorView.addImage(bitmap);
}
});
API介绍和原理解析
概念:扩展的观察者模式
RxJava 的异步实现,是通过一种扩展的观察者模式来实现的
观察者模式面向的需求是:
- A对象(观察者)对B对象(被观察者)的某种变化高度敏感,需要在B变化的一瞬间做出反应。
例如:警察抓小偷,警察是观察者,需要时刻盯着小偷的一举一动,并在需要小偷伸手作案的时候实施抓捕。
程序的观察者模式与真正的观察略有不同,观察者不需要时刻盯着被观察者,而是采用**注册(Register)或者成为订阅(Susscribe)**的方式,告诉被观察者,我们需要你的某某状态,你要在它变化的时候通知我。
Android中典型的观察者模式是点击监听器OnClickListener,对于View设置监听来说,View是被观察者,OnClickListener是观察者,二者通过setOnClickListener 方法达成订阅关系。订阅之后用户点击按钮的瞬间,Android Framework 就会将点击事件发送给已经注册的 OnClickLisener 。采用 这样被动的观察方式,省去了反复检索状态的资源消耗,越能够得到更高的反馈速度。
而RxJava作为一个工具库,使用的是通用形式的观察者模式。
RxJava的观察者模式
RxJava有四个基本概念:
- Observable(可观察者,即被观察者)
- Observer(观察者)
- subscribe(订阅)
- 事件
Observable、Observer 通过subscribe方法实现订阅关系,从而Observable 可以在需要的时候发出事件来通过 Observer
与传统的观察者模式不同,RxJava的事件回调方法除了普通事件onNext() (相当于 onClick / onEvent() )之外,还定义了两个特殊的事件:onCompleted 和 onError()
- onCompleted():事件队列完结。RxJava不仅把每个事件单独处理,还会把它们看做一个队列。RxJava规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为结束标志
- onError():事件队列异常。在事件处理过程中出现异常,OnError() 会被触发,同时队列自动终止,不允许再有事件发出。
- 在一个正确运行的事件序列中,onCompleted() 和 onError() 有且只能有一个,而且是事件序列中的最后一个。需要注意的是: onCompleted() 和 onError() 二者也是排斥的,即在队列中调用其中一个,就不应该再调用另一个。
RxJava的观察者模式大致如下图:
基本实现
基于以上的概念,RxJava的基本实现主要有三点:
1)创建Observer
Obserber 即 观察者,它决定事件触发的时候将有怎样的动作。RxJava中的 Observer 接口的实现方式:
Observer<String> observer = new Observer<String>(){
@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<String> subscriber = new Subscriber<String>(){
@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!");
}
};
不仅基本使用方式一样,实质上,在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)创建Observable
Observable 即被观察者,它决定什么时候触发事件以及触发怎样的事件。RxJava 使用 create() 方法来创建一个 Observable,并为它定义事件触发规则:
Observable observable = Observable.create(new Observable.OnSubscribe<String>(){
@Override
public void call(Subscriber<? super String> subscriber){
subscriber.onNext("Hello"); //onNext 相当于 onClick,onEvent 等
subscriber.onNext("Hi");
subscriber.onNext("Aloha");
subscriber.onCompleted();
}
}
可以看到,这里传入了一个 OnSubscribe 对象作为参数。 OnSubscribe 会被储存在返回的 Observale 对象中,它的作用相当于一个计划表,当 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<? extends T>):将传入的数组或Iterable 拆分成具体对象后,一次发送出来
String[] words = {"Hello","Hi","Aloha"}; Observable observable = Observable.from(words); //将会一次执行 //onNext("Hello"); //onNext("Hi"); //onNext("Aloha"); //onCompleted();
上面 just(T…) 的例子和 from(T[]) 的例子,都和之前 *create(OnSubscribe)的例子是等价的。
3)Subscribe(订阅)
创建了 Observable 和 Observaber 之后,再用 *subscribe()*方法将它们联结起来,整条链子就可以工作了。代码形式很简单:
observable.subscribe(observer);
//或者:
observable.subscribe(subscriber);
其中,Observable.subscribe(Subscriber)的内部实现是这样的(仅核心代码):
//注意,这不是 subscribe 的源码,而是将源码中与 性能、兼容性、扩展性有关的代码剔除后的核心代码
//如果需要看源码,可以去 RxJava 的 Github 仓库下载
public Subscription subscribe(Subscriber subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
return subscriber;
}
可以看到, subscrite() 做了三件事:
- 调用Subscriber.onStart() ,这个方法在前面已经介绍了,是一个可选的准备方法
- 调用Observable中的OnSubscribe.call(Subscriber)
。在这里,事件发送的逻辑开始运行,从这可以看到,在RxJava中,Observable
并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当*subscribe()*方法执行的时候。 - 将传入的Subscriber 作为 Subscription 返回,这是为了方便 unsubscribe()
整个过程中对象之间的关系:
场景示例
打印字符串数组
将字符串数组 names 中的所有字符串依次打印出来:
String names = ...;
Observable.from(names)
.subscribe(new Subscriber<String>(){
@Override
public void onNext(String){
Log.i("mylog",String);
}
@Override
public void onCompleted(){
}
@Override
public void onError(Throwable e){
}
});
但是我们只需要用到 onNext方法,还有两个重写的onError 和 onCompleted 并没有用,这样导致我们多出了几行根本不用的代码。于是可以用 Action 代替Subscriber得到的代码是:
String[] names = ..;
Observable.from(names) //返回被观察者
.subscribe(new Action1<String>(){
@Override
public void call(String name){
Log.d(tag, name);
}
});
什么是Action
Acton 是 RxJava的一个接口,常用的有Action0 和 Action1
- Action0:它只有一个Call 方法,这个方法是无参无返回值的;由于onCompleted 方法也是无参无返回值的,因此Action0 可以被当成一个包装对象
- Action1:它同样只有一个 call(T param) 方法,这个方法也没有返回值,但有一个参数,与Action0同理,由于 onNext(T obj) 和 onError(Throwable error) 也是单参数无返回值,因此Action1可以将onNext和 onError打包起来传入subscribe() 以实现不完整定义的回调
Action的使用
定义三个对象,分别打包onNext(obj)、onError(error)、onCompleted()
Observable observable = Observable.just("Hello", "World");
//处理onNext()中的内容
Action1<String> onNextAction = new Action1<String>() {
@Override
public void call(String s) {
Log.i(TAG, s);
}
};
//处理onError()中的内容
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
}
};
//处理onCompleted()中的内容
Action0 onCompletedAction = new Action0() {
@Override
public void call() {
Log.i(TAG, "Completed");
}
};
接下来使用subscribe重载的方法
//使用 onNextAction 来定义 onNext
Observable.just(“Hello”,"World").subscribe(onNextAction);
//使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
Observable.just("Hello", "World").subscribe(onNextAction, onErrorAction);
//使用 onNextAction、 onErrorAction 和 onCompletedAction 来定义 onNext()、 onError() 和 onCompleted()
Observable.just("Hello", "World").subscribe(onNextAction, onErrorAction, onCompletedAction);
那为什么使用Action也能达到使用Subscriber的结果?源码如下:
原理很简单,就是通过传入的Action的参数数量来决定其内部Subscriber执行的方法,其根本还是在执行Subscriber的三个方法。
由id取得图片并显示
有指定的drawable 文件id drawableRes 取得图片,并显示在 ImageView中,并在出现异常的时候打印Toast报错:
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();
}
}).subscribe(new Observer<Drawable>(){
@Override
public void onNext(Drawable drawable){
}
@Override
public void onCompleted(){
}
@Override
public void onErro(Throwable e){
}
});
正如上面两个例子,创建出 Observable 和 Observer ,在用 subscribe() 将它们串起来,一次RxJava 的基本使用就完成了
然而,在RxJava的默认规则中,事件的发出和消费都是在同一个线程的,也就是说,如果只用上面的方法,实现出来的只是一个同步的观察者模式。观察者模式本身的目的就是为了完成异步机制,因此异步对于RxJava来说非常重要,而要实现异步,则需要用到RxJava的另一个概念:Scheduler
Scheduler(调度器)—线程控制
Scheduler的API
在RxJava中,Scheduler -------调度器,相当于线程控制器,RxJava通过它来指定每一段代码应该运行在什么样的线程。RxJava已经内置了几个Scheduler ,它们已经适合大多数的使用场景:
- Schedulers.immediate():直接在当前线程运行,相当于不指定线程,这是默认的Shceduler
- Schedulers.newThread() :总是启用新线程,并在新线程执行操作
- Schedulers.io():I/0操作(读写文件,读写数据库,网络信息交互等)所使用的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() :指定subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。
observeOn():指定Subscriber 所运行的线程,或者叫事件消费的线程。
示例代码
Observable.just(1,2,3,4)
.subscribeOn(Schedulers.io) //指定订阅发生在IO线程
.observeOn(AndroidSchedulers.mainThread()) //指定Subscriber 的回调发生在主线程
.subscrite(new Action1<Integer>(){
@Override
public void call(Integer number){
Log.d(tag, "number:"+number);
}
});
上段代码中,被创建的事件的内容将会在IO线程发出,而数据的回调将在主线程中,这种适用于多少的【后台线程取数据,主线程显示】
Scheduler 原理
变换
API
变换的原理:lift()
compose:对 Observable 整体的变换
RxJava 适合场景和使用方式
参考:https://gank.io/post/560e15be2dca930e00da1083#toc_1
参考:https://gank.io/post/560e15be2dca930e00da1083