RxJava学习笔记
什么是RxJava—实现异步操作的库
一个在Java VM上使用可观测的序列来组成异步的、基于事件的程序的库
优点
简洁,随着程序逻辑变得越来越复杂,它依然能够保持简洁
原理简析
RxJava的异步实现是通过一种扩展的观察者模式来实现的
观察者模式:观察者通过注册(订阅)的方式告诉被观察者,你必须在某个状态发生变化时通知我(eg:安卓中的点击监听)
RxJava的观察者模式
- Observable(被观察者)
- Observer(观察者)
- subscribe(订阅)
- 事件
RxJava的事件回调除了普通事件onNext()(相当于onClick()/onEvent())之外,还定义了两个特殊的事件:onCompleted()和onError()
- onCompleted():事件队列完结,RxJava不仅把每个事件单独处理,还会把它们看做一个队列(当不会再有新的onNext()发出时,需要触发onCompleted()方法作为标志)
- onError():事件队列异常,在事件处理过程中出现异常时触发,同时队列自动终止
基本实现
1)创建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!"); } };
Observer和Subscriber的区别
1.onStart():这是Subscriber增加的方法,会在事件未发送之前被调用,可以用于做一些准备工作(eg:数据清空),默认实现为空
2.unsubscribe():这是Subscriber实现Subscription接口的方法,用于取消订阅。isUnsubscribed()用了判断状态,unsubscribe()方法一般放在onPause/onStop()方法中来解除引用关系,避免内存泄露
2)创建Observable(被观察者,决定什么时候触发事件以及触发怎样的事件)
Observable observable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello"); subscriber.onNext("Hi"); subscriber.onNext("Aloha"); subscriber.onCompleted(); } });
当Observable被订阅的时候,OnSubscribe的call()方法会自动被调用,事件序列会依照设定依次触发,实现了由被观察者调用观察者的回调方法,完成由被观察者向观察者的事件传递
快捷创建事件队列的方法:
//just(T...): 将传入的参数依次发送出来 Observable observable = Observable.just("Hello", "Hi", "Aloha"); //from(T[]) / from(Iterable<? extends T>) : 将传入的数组或 Iterable 拆分成具体对象后,依次发送出来 String[] words = {"Hello", "Hi", "Aloha"}; Observable observable = Observable.from(words);
3)Subscribe(订阅,创建了Observable和Observer之后,再用subscribe()方法将它们联结起来)
observable.subscribe(observer); // 或者: observable.subscribe(subscriber);
内部实现(核心代码):
public Subscription subscribe(Subscriber subscriber) { subscriber.onStart(); onSubscribe.call(subscriber); return subscriber; }
示例:
由指定的一个 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) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
创建被观察者——>被观察者中决定触发什么样的事件及触发的顺序——>订阅观察者(new Observer)——>在观察者中执行具体的操作
线程控制——Scheduler
默认情况下,RxJava遵循线程不变的原则,哪个线程调用subscribe()就在哪个线程生产和消费事件,如需切换线程,就要用到Scheduler(调度器)
Scheduler的API
- Schedulers.immediate():直接在当前线程运行,默认的Scheduler
- Schedulers.newThread():总是启用新线程,并在新线程中执行操作
- Schedulers.io():I/O操作(读写文件、读写数据库、网络信息交互等)所使用的Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。
- Schedulers.computation():计算所使用的Scheduler。这个 Scheduler 使用固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
- Android中还有一个专用的AndroidSchedulers.mainThread(),它指定的操作将在Android主线程运行
示例:
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) {
Log.d(tag, "number:" + number);
}
});
变换
RxJava提供了对事件序列进行变换的支持,这是它的核心功能之一。所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列
示例:map()
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和Action1的区别在于,Func1包装的是有返回值的方法。
示例中,map()方法将参数中的String对象转换成一个Bitmap对象后返回
示例:flatMap()
Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
@Override
public void onNext(Course course) {
Log.d(tag, course.getName());
}
...
};
Observable.from(students)
.flatMap(new Func1<Student, Observable<Course>>() {
@Override
public Observable<Course> call(Student student) {
return Observable.from(student.getCourses());
}
})
.subscribe(subscriber);
示例中是通过传入一组Student对象,打印出每个Student选择的Course,这里用map()显然是不行的,因为map()是一对一的转化。
这个时候就要用到flatMap()了,flatMap()也是把传入的参数转化后返回另一个对象,但和map()不同的是flatMap()返回的是个Observable对象,并且这个Observable对象并不是被直接发送到了Subscriber的回调方法中。而是利用这个Observable发送事件,这些事件会被汇入同一个Observable,而这个Observable负责将这些事件统一交给Subscriber的回调方法。
flatMap()还常用于嵌套的异步操作,如Retorfit+RxJava
networkClient.token() // 返回 Observable<String>,在订阅时请求 token,并在响应后发送 token
.flatMap(new Func1<String, Observable<Messages>>() {
@Override
public Observable<Messages> call(String token) {
// 返回 Observable<Messages>,在订阅时请求消息列表,并在响应后发送请求到的消息列表
return networkClient.messages();
}
})
.subscribe(new Action1<Messages>() {
@Override
public void call(Messages messages) {
// 处理显示消息列表
showMessages(messages);
}
});
线程的自由控制
利用subscribeOn()结合observeOn()可以实现线程的控制,让事件的产生和消费发生在不同的线程。其中subscribeOn()的线程控制可以从事件发出的开端就造成影响,而observeOn()控制的是它后面的线程,示例:
Observable.just(1, 2, 3, 4) // IO 线程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.map(mapOperator) // 新线程,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(mapOperator2) // IO 线程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread)
.subscribe(subscriber); // Android 主线程,由 observeOn() 指定
如上,通过observeOn()的多次调用,程序实现了线程的多次切换,不同于observeOn(),subscribeOn()的位置放在哪里都可以,但是只能调用一次
延伸:doOnSubscribe()
Subscriber的onStart()可以用作流程开始前的初始化,但是不能指定线程,而Observable.doOnSubscribe()同样是在subscribe()调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);