Rxjava复习专用
Rxjava —— 四个基本概念
- RxJava 有四个基本概念:Observable (可观察者,即被观察者)、 Observer (观察者)、 subscribe (订阅)、事件。
- Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。
Observer —— 观察者
它决定事件触发的时候将有怎样的行为
- Observer 接口
- Subscriber 抽象类,实现了Observer和Subscription
不仅基本使用方式一样,实质上,在 RxJava 的 subscribe 过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。所以如果你只想使用基本功能,选择 Observer 和 Subscriber 是完全一样的。它们的区别对于使用者来说主要有两点:
两者的区别:
1.Subscriber 多了 onStart( ) 方法,但是该方法发生在 subscribe 发生的线程,不能指定线程
2.Subscriber 多了 unsubscribe( ) 方法, Subscriber 所实现的另一个接口 Subscription 的方法,用于取消订阅
示例代码:
Subscriber subscriber = new Subscriber() {
@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!");
}
};
Observable —— 被观察者
它决定什么时候触发事件以及触发怎样的事件
示例代码:
- create( ) RxJava 最基本的创造事件序列的方法
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
//观察者将会依次调用三次onNext和一次Completed
subscriber.onNext("Hello");
subscriber.onNext("Hi");
subscriber.onNext("Aloha");
subscriber.onCompleted();
}
});
- just( T... ) 将传入的参数依次发送出来。
Observable observable = Observable.just("Hello", "Hi", "Aloha");
// 将会依次调用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
- from(T[ ])
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
// 将会依次调用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
Subscribe —— 订阅
创建了 Observable 和 Observer 之后,再用 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;
}
调用 Subscriber.onStart() 。这个方法在前面已经介绍过,是一个可选的准备方法。
调用 Observable 中的 OnSubscribe.call(Subscriber) 。在这里,事件发送的逻辑开始运行。从这也可以看出,在 RxJava 中, Observable 并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当 subscribe() 方法执行的时候。
将传入的 Subscriber 作为 Subscription 返回。这是为了方便 unsubscribe().
除了 subscribe(Observer) 和 subscribe(Subscriber) ,subscribe() 还支持不完整定义的回调,RxJava 会自动根据定义创建出 Subscriber 。形式如下:
Action1 onNextAction = new Action1() {
// onNext()
@Override
public void call(String s) {
Log.d(tag, s);
}
};
Action1 onErrorAction = new Action1() {
// 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() {
@Override
public void call(String name) {
Log.d(tag, name);
}
});
- 由 id 取得图片并显示
由指定的一个 drawable 文件 id drawableRes 取得图片,并显示在 ImageView 中,并在出现异常的时候打印 Toast 报错:
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe() {
@Override
public void call(Subscriber super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
}).subscribe(new Observer() {
@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();
}
});
以上为RxJava的基本使用,不涉及线程切换.以下开始切换
线程控制 —— Scheduler
相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程
- Scheduler的API
- 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 主线程运行。
有了这几个 Scheduler,就可以使用
- subscribeOn( )
- observeOn( )
两个方法来对线程进行控制了。subscribeOn( ) : 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。observeOn( ) : 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
示例代码:非常常见,它适用于多数的 『后台线程取数据,主线程显示』的程序策略。
//将图片路径转为bitmap
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Action1() {
@Override
public void call(Integer number) {
Log.d(tag, "number:" + number);
}
});
变换 —— 将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列
API
- map( ) 事件对象的一对一的变换
//将图片路径转为bitmap
Observable.just("images/logo.png") // 输入类型 String
.map(new Func1() {
@Override
public Bitmap call(String filePath) { // 参数类型 String
return getBitmapFromPath(filePath); // 返回类型 Bitmap
}
})
.subscribe(new Action1() {
@Override
public void call(Bitmap bitmap) { // 参数类型 Bitmap
showBitmap(bitmap);
}
});
- flatMap( ) 一堆对象变成另一堆对象
//假设有一个数据结构『学生』,现在需要打印出每个学生所需要修的所有课程的名称
Student[] students = ...;
Subscriber subscriber = new Subscriber() {
@Override
public void onNext(Course course) {
Log.d(tag, course.getName());
}
...
};
Observable.from(students)
.flatMap(new Func1>() {
@Override
public Observable call(Student student) {
return Observable.from(student.getCourses());//这句代码
}
})
.subscribe(subscriber);
解析:
将一堆对象拆分,变成一个个对象,再以一个对象为单位创建观察者,Observable.from(student.getCourses())这句代码看出,新建的观察者发送事件.全部汇入Subscriber的回调中
- flatMap() 和 map() 有一个相同点:它也是把传入的参数转化之后返回另一个对象。
- 但需要注意,和 map() 不同的是, flatMap() 中返回的是个 Observable(观察者) 对象,并且这个 Observable 对象并不是被直接发送到了 Subscriber 的回调方法中。
- flatMap() 的原理是这样的:
- 使用传入的事件对象创建一个 Observable 对象;
- 并不发送这个 Observable, 而是将它激活,于是它开始发送事件;
- 每一个创建出来的 Observable 发送的事件,都被汇入同一个 Observable ,而这个 Observable 负责将这些事件统一交给 Subscriber 的回调方法。这三个步骤,把事件拆成了两级,通过一组新创建的 Observable 将初始的对象『铺平』之后通过统一路径分发了下去。而这个『铺平』就是 flatMap() 所谓的 flat。
这里略过了变换的原理
- compose( ) 一堆被观察者都需要变化相同的逻辑,使用此方法
//假设在程序中有多个 Observable ,并且他们都需要应用一组相同的 lift() 变换。
public class LiftAllTransformer implements Observable.Transformer {
@Override
public Observable call(Observable 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);
线程控制 —— 线程的自由控制
代码示例
Observable.just(1, 2, 3, 4) // IO 线程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())//===指定了事件发生的线程. subscribeOn() 的位置放在哪里都可以,但它是只能调用一次的。
.observeOn(Schedulers.newThread())//================指定了接下来map发送的线程
.map(mapOperator) // 新线程,由 observeOn() 指定
.observeOn(Schedulers.io())//=======================指定了接下来map发送的线程
.map(mapOperator2) // IO 线程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread) //==========制定了接下来消费事件的线程
.subscribe(subscriber); // Android 主线程,由 observeOn() 指定
解析:
observeOn() 指定的是 Subscriber(观察者) 的线程,而这个 Subscriber(观察者) 并不是(严格说应该为『不一定是』,但这里不妨理解为『不是』)subscribe() 参数中的 Subscriber(观察者),而是 observeOn() 执行时的当前 Observable 所对应的 Subscriber(观察者),即它的直接下级 Subscriber(观察者) 。换句话说,observeOn() 指定的是它之后的操作所在的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次 observeOn() 即可
意思就是:RxJava中对Observable进行变化操作都会有对应的Subscriber(观察者) ,我们在操作前设置线程observeOn()就可以指定接下来的Subscriber(观察者)
延伸:doOnSubscribe()
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);
解析:
它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。
差不多了,该去实际场景中使用了
复习链接
给 Android 开发者的 RxJava 详解 -- [作者:扔物线]