该笔记通过“RxJava 详解”学习记录
学习RxJava之前需要了解“观察者模式”
RxJava四个基本概念
1、Observable — 被观察者
2、Observer — 观察者
3、Subscribe — 订阅
4、事件
RxJava有两种事件:
1、普通事件 onNext()
2、特殊事件 onCompleted() — 时间队列完结。当不会再有新的onNext()发出时,需出发onCompleted()方法作为标志。
onError() — 事件队列异常。当事件处理过程中出现异常时,onError会被触发,同时队列自动终止。
注:一个正确的事件队列中,onCompleted()和onError()有且只有一个,并且是事件序列中的最后一个。onCompleted()与onError()二者互斥,即在队列中调用了其中一个,就不应该在调用另一个。
创建Observer,即观察者
它决定事件触发时将有怎样的行为。
Observer<String> observer = new Observer<String>(){
@Override
public void onNext(String s){ Log.d(tag,s); }
@Override
public void onCompleted(){ Log.d(tag,"Completed!"); }
@Override
public void onError(Throwable e){ Log.d(tag,s); }
}
或可以
创建Subscriber,即订阅者
它是Observer的抽象类,对Observer接口进行了拓展。
Subscriber<String> subscriber = new Subscriber<String>(){
@Override
public void onNext(String s){ Log.d(tag,s); }
@Override
public void onCompleted(){ Log.d(tag,"Completed!"); }
@Override
public void onError(Throwable e){ Log.d(tag,s); }
}
Observer与Subscriber之间的区别:
1.onStart():这是Subscriber增加的方法。它会在subscribe(订阅)刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求(例如弹出一 个显示进度的对话框,这必须在主线程执行),onStart()就不适用了,因为它总是在subscribe(订阅)所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法。
2.unsubscribe():这是Subscriber所实现的另一个接口Subscription的方法,用于取消订阅。在这个方法被调用后,Subscriber将不再接收事件。一般在这个方法调用前,可以使用isUnsubscribed()先判断一下状态。unsubscribe()这个方法很重要,因为在subscribe()之后,Observable会持有Subscriber的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。
创建Observable,即被观察者
它决定什么时候触发时间以及吃法怎样的时间。
1.使用create()方法创建
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();
}
});
对create()方法的详解:这里传入一个OnSubscribe对象作为参数。OnSubscribe会被存储在返回的Observale对象中,他的作用相当于一个计划表,当Observable被订阅时,OnSubscribe的call()方法会自动被调用,事件序列就会依照设定依次触发。
2.使用just(T…)方法创建。将传入的参数依次发送出来。
Observable observable = Observable.just("Hello", "Hi", "Aloha");
3.使用from(T[])/from(Iterable
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
Subscribe(订阅)
observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);
为什么不能颠倒顺序,即为什么不能observer.subscribe(obserable):
Observable.subscribe(Subscriber)内部核心代码
public Subscription subscribe(Subscriber subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
return subscriber;
}
可以看到,subscribe()做了3件事:
1.调用Subscriber.onStart()方法。
2.调用Observable中的Onsubscribe.call(Subscriber)。在这里,事件发送的逻辑开始运行。从这也可以看出,在 RxJava 中,Observable 并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当 subscribe() 方法执行的时候。
3.将传入的Subscriber作为Subscription返回,方便unsubscribe()。
subscribe()不完整定义的回调
Action1<String> onNextAction = new Action1<String>() {
// onNext()
@Override
public void call(String s) {
Log.d(tag, s);
}
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
// onError()
@Override
public void call(Throwable throwable) {
// Error handling
}
};
Action0 onCompletedAction = new Action0() {
// onCompleted()
@Override
public void call() {
Log.d(tag, "completed");
}
};
// 自动创建 Subscriber ,并使用 onNextAction 来定义 onNext()
observable.subscribe(onNextAction);
// 自动创建 Subscriber ,并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
observable.subscribe(onNextAction, onErrorAction);
// 自动创建 Subscriber ,并使用 onNextAction、 onErrorAction 和 onCompletedAction 来定义 onNext()、 onError() 和 onCompleted()
observable.subscribe(onNextAction, onErrorAction, onCompletedAction);
举例:
// 将字符串数组 names 中的所有字符串依次打印出来:
String[] names = ...;
Observable.from(names)
.subscribe(new Action1<String>() {
@Override
public void call(String name) {
Log.d(tag, name);
}
});
//由指定的一个 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();
}
})
线程控制 — Scheduler (调度器)
- Schedulers.immediate():在当前线程运行。默认方法。
- Schedulers.newThread():启用新线程,并在新线程执行操作。
- Schedulers.io():I/O(读写文件、读写数据库、网络交互)操作所用。和newThread()差不多,区别在于io()的内部实现使用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下io()比newThread()更有效率。不要把计算工作放在io()中,可以避免创建不必要的线程。
- Schedulers.computation():计算所使用的Scheduler。该计算指CPU密集型计算,即不会被I/O等操作限制性能操作,例如图形计算。该方法使用固定线程池,大小为CPU核数。不要把I/O操作放到该方法中,否则I/O操作的等待时间会浪费CPU。
- AndroidSchedulers.mainThread():Android专有方法,指定操作在Android主线程运行。
以上方法可以使用subscribeOn()和observeOn()两个方法来控制。
- subscribeOn():指定subscribe所发生的线程,即Observable.OnSubscibe被激活时所处的线程。可叫作事件产生的线程。
- observeOn():指定Subscriber所运行的线程。可叫作事件消费的线程。
举例:
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.通过observeOn()可以实现多次切换线程。若想要多次切换线程,只需要在要切换线程的位置调用一次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() 指定
2.subscribeOn()的位置可以随便放,但它只能调用一次。
延伸:doOnSubscribe()
Subscriber 的 onStart() 可以用作流程开始前的初始化。然而 onStart() 由于在 subscribe() 发生时就被调用了,因此不能指定线程,而是只能执行在 subscribe() 被调用时的线程。这就导致如果 onStart() 中含有对线程有要求的代码(例如在界面上显示一个 ProgressBar,这必须在主线程执行),将会有线程非法的风险,因为有时你无法预测 subscribe() 将会在什么线程执行。
而与 Subscriber.onStart() 相对应的,有一个方法 Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同样是在 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);
变换
1.事件对象变换 map(),例如:
Observable.just("images/logo.png") // 输入类型 String
.map(new Func1<String, Bitmap>() { //将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);
}
});
2.事件队列变换 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);
3.针对Observable自身变换 compose(Transformer),例如:
/**
* 需求:假设程序中有多个 Observable,并且都需要应用一组相同的 lift() 变换
*/
public class LiftAllTransformer implements Observable.Transformer<Integer, String> {
@Override
public Observable<String> call(Observable<Integer> observable) {
return observable
.lift1()
.lift2()
.lift3()
.lift4();
}
}
...
Transformer liftAll = new LiftAllTransformer();
observable1.compose(liftAll).subscribe(subscriber1);
observable2.compose(liftAll).subscribe(subscriber2);
observable3.compose(liftAll).subscribe(subscriber3);
observable4.compose(liftAll).subscribe(subscriber4);
另附几篇博客参考:
RxJava源码初探:http://www.apkbus.com/blog-705730-60688.html
RxJava全面总结:http://www.apkbus.com/blog-705730-60454.html
玩透RxJava基础知识:http://www.apkbus.com/blog-705730-60437.html