众所周知,rxjava是一种响应式编程,也可以叫做观察者模式,简单来形容一下就是:
观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主题是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知
直接上代码:(本篇的代码中部分使用了lambda,看的时候如果费解,望谅解)
/**
* 被观察者
* 1.0泛型为被操作的类型
* 1.1emitter称为发射器
* 1.2有三种发射的方法,分别是
* void onNext(T value)、void onError(Throwable error)、onComplete(),
* onNext方法可以无限调用,Observer(观察者)所有的都能接收到,
* onError和onComplete是互斥的,Observer(观察者)只能接收到一个,
* OnComplete可以重复调用,但是Observer(观察者)只会接收一次,
* 而onError不可以重复调用,第二次调用就会报异常。
*/
static Observable mObservable = Observable.create((ObservableOnSubscribe<String>) emitter -> {
emitter.onNext("连载1");
emitter.onNext("连载2");
emitter.onNext("连载3");
emitter.onComplete();
});
被观察者通过发射器emitter发射 onNext、onError、onComplete三种事件,读者通过订阅不同的事件作出不同的响应
/**
* 观察者
*/
static Observer<String> reader = new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
//处理观察者和被观察者的,如果观察者不想再订阅了,请调用
//d.dispose();
System.out.println("onSubscribe---" + d.isDisposed());
//CompositeDisposable类
//1、可以快速解除所有添加的Disposable类.
//2、每当我们得到一个Disposable时就调用CompositeDisposable.add()将它添加到容器中,
//在退出的时候, 调用CompositeDisposable.clear() 即可快速解除.
}
//跟emitterd对应
@Override
public void onNext(String s) {
System.out.println("onNext---" + s);
}
//跟emitterd对应
@Override
public void onError(Throwable e) {
System.out.println("onError---" + e.getMessage());
}
//跟emitterd对应
@Override
public void onComplete() {
System.out.println("onComplete---");
}
};
最后,我们在两者之间建立订阅关系
/**
* 观察者被观察者建立订阅关系
* 只有观察者和被观察者建立了订阅关系才会如此
*/
mObservable.subscribe(reader);
//此处插播一句 被观察者可以有一个空订阅
//mObservable.subscribe();
我们使用rxjava非常重要的一个原因就是它能非常方便的进行线程切换,不像上边那样的建立被观察者,观察者然后建立订阅关系,本次示例,我们使用链式调用来展示rxjava的线程切换
/**2.0
*rxjava调度器Scheduler以及rxjava的链式调用
* 2.1多次指定上游的线程(执行的线程subscribeOn)只有第一次指定的有效,
* 也就是说多次调用subscribeOn() 只有第一次的有效, 其余的会被忽略.
* 2.2多次指定下游的线程是可以的,
* 也就是说每调用一次observeOn() , 下游的线程就会切换一次.
* 关于线程
* 在RxJava中, 已经内置了很多线程选项供我们选择, 例如有:
* Schedulers.io() 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作
* Schedulers.computation() 代表CPU计算密集型的操作, 例如需要大量计算的操作
* Schedulers.newThread() 代表一个常规的新线程
* AndroidSchedulers.mainThread() 代表Android的主线程
*/
Observable.create((ObservableOnSubscribe<String>) emitter -> {
emitter.onNext("-----连载1");
emitter.onNext("-----连载2");
emitter.onNext("-----连载3");
emitter.onComplete();
}).observeOn(AndroidSchedulers.mainThread())//回调在主线程
.subscribeOn(Schedulers.io())//执行在io线程
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
//处理观察者和被观察者的,如果观察者不想再订阅了,请调用
//d.dispose();
System.out.println("-----onSubscribe---"+d.isDisposed());
}
//跟emitterd对应
@Override
public void onNext(String s) {
System.out.println("-----onNext---"+s);
}
//跟emitterd对应
@Override
public void onError(Throwable e) {
System.out.println("-----onError---"+e.getMessage());
}
//跟emitterd对应
@Override
public void onComplete() {
System.out.println("------onComplete---");
}
});
观察者可以只订阅onnext方法
mObservable.subscribe(new Consumer() {
@Override
public void accept(Object o) throws Exception {
System.out.println("accept---" + o);
}
});
rxjava另一个神奇的操作地方在于操作符,比如
map、flatMap、concatMap
/**rvjava操作符
* 5.1map变换操作符
*/
Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}).map(integer -> "This is result " + integer).subscribe(s -> {
System.out.println("accept---" + s);
});
/**
*@describe 5.2flatMap操作符
* FlatMap将一个发送事件的上游Observable变换为多个发送事件的Observables,
* 然后将它们发射的事件合并后放进一个单独的Observable里
* 不保证顺序
*/
Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}).flatMap((Function<Integer, ObservableSource<String>>) integer -> {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("I am value " + integer);
}
return Observable.fromIterable(list).delay(10, TimeUnit.MILLISECONDS);
}).subscribe(s -> {
System.out.println("accept---" + s);
});
/**
*@describe 5.3concatMap操作符
* concatMap将一个发送事件的上游Observable变换为多个发送事件的Observables,
* 然后将它们发射的事件合并后放进一个单独的Observable里
* flatMap操作符的作用就是把一个Observable转换为另一个Observable
* 比如先登录后注册
* 保证顺序
*/
Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}).concatMap((Function<Integer, ObservableSource<String>>) integer -> {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("I am value " + integer);
}
return Observable.fromIterable(list).delay(10, TimeUnit.MILLISECONDS);
}).subscribe(s -> {
System.out.println("accept---" + s);
});
/**
*@describe 5.4concatMap操作符
* concatMap将一个发送事件的上游Observable变换为多个发送事件的Observables,
* 然后将它们发射的事件合并后放进一个单独的Observable里
* 保证顺序
* 接收和失败
*/
Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}).concatMap((Function<Integer, ObservableSource<String>>) integer -> {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("I am value " + integer);
}
return Observable.fromIterable(list).delay(10, TimeUnit.MILLISECONDS);
}).subscribe(s -> {
System.out.println("accept---" + s);
}, throwable -> {
System.out.println("throwable---" + throwable.getMessage());
});
/**
*@describe 5.5zip操作符
* Zip通过一个函数将多个Observable发送的事件结合到一起,
* 然后发送这些组合到一起的事件. 它按照严格的顺序应用这个函数。
* 它只发射与发射数据项最少的那个Observable一样多的数据
* 比如页面数据需要几个接口返回数据
* 可以通过zip来打包请求 ,返回所有数据
*/
Observable observable1 = Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onNext(4);
emitter.onComplete();
}).subscribeOn(Schedulers.io());
Observable observable2 = Observable.create((ObservableOnSubscribe<String>) emitter -> {
emitter.onNext("A");
emitter.onNext("B");
emitter.onNext("C");
emitter.onComplete();
}).subscribeOn(Schedulers.io());
Observable.zip(observable1, observable2, (BiFunction<Integer, String, String>) (integer, s) -> integer+s)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(o -> {
System.out.println("accept---" + o);
}, throwable -> {
});
组合的过程是分别从 两根水管里各取出一个事件 来进行组合, 并且一个事件只能被使用一次, 组合的顺序是严格按照事件发送的顺利 来进行的, 也就是说不会出现圆形1 事件和三角形B 事件进行合并, 也不可能出现圆形2 和三角形A 进行合并的情况.
最终下游收到的事件数量 是和上游中发送事件最少的那一根水管的事件数量 相同
组合操作符:merge,zip,join
merge、 Join
Join操作符
join(Observable, Func1, Func1, Func2)我们先介绍下join操作符的4个参数:
Observable:源Observable需要组合的Observable,这里我们姑且称之为目标Observable;
Func1:接收从源Observable发射来的数据,并返回一个Observable,
这个Observable的声明周期决定了源Obsrvable发射出来的数据的有效期;
Func1:接收目标Observable发射来的数据,并返回一个Observable,
这个Observable的声明周期决定了目标Obsrvable发射出来的数据的有效期;
Func2:接收从源Observable和目标Observable发射出来的数据,并将这两个数据组合后返回。
所以Join操作符的语法结构大致是这样的:onservableA.join(observableB, 控制observableA发射数据有效期的函数, 控制observableB发射数据有效期的函数,两个observable发射数据的合并规则)
join操作符的效果类似于排列组合,把第一个数据源A作为基座窗口,他根据自己的节奏不断发射数据元素,第二个数据源B,每发射一个数据,我们都把它和第一个数据源A中已经发射的数据进行一对一匹配;举例来说,如果某一时刻B发射了一个数据“B”,此时A已经发射了0,1,2,3共四个数据,那么我们的合并操作就会把“B”依次与0,1,2,3配对,得到四组数据: [0, B] [1, B] [2, B] [3, B]
示例:
final List<House> houses = DataSimulator.getHouses();//模拟的房源数据,用于测试
//用来每秒从houses总取出一套房源并发射出去
Observable<House> houseSequence =
Observable.interval(1, TimeUnit.SECONDS)
.map(new Func1<Long, House>() {
@Override
public House call(Long position) {
return houses.get(position.intValue());
}
}).take(houses.size());//这里的take是为了防止houses.get(position.intValue())数组越界
//用来实现每秒发送一个新的Long型数据
Observable<Long> tictoc = Observable.interval(1, TimeUnit.SECONDS);
houseSequence.join(tictoc,
new Func1<House, Observable<Long>>() {
@Override
public Observable<Long> call(House house) {
//控制houseSequence延迟两秒发射一次数据
return Observable.timer(2, TimeUnit.SECONDS);
}
},
new Func1<Long, Observable<Long>>() {
@Override
public Observable<Long> call(Long aLong) {
//控制tictoc不延迟发送数据
return Observable.timer(0, TimeUnit.SECONDS);
}
},
new Func2<House, Long, String>() {
@Override
public String call(House house, Long aLong) {
//数据合并
return aLong + "-->" + house.getDesc();
}
}
).subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.exit(0);
}
@Override
public void onError(Throwable e) {
System.out.println("Error:"+e.getMessage());
}
@Override
public void onNext(String s) {
System.out.println(s);
}
});
打印结果:
0-->中粮海景壹号新出大平层!总价4500W起
1-->中粮海景壹号新出大平层!总价4500W起
1-->满五唯一,黄金地段
2-->中粮海景壹号新出大平层!总价4500W起
2-->满五唯一,黄金地段
2-->一楼自带小花园
3-->一楼自带小花园
3-->毗邻汤臣一品
4-->毗邻汤臣一品
4-->顶级住宅,给您总统般尊贵体验
5-->顶级住宅,给您总统般尊贵体验
5-->顶层户型,两室一厅
6-->顶层户型,两室一厅
6-->南北通透,豪华五房
7-->南北通透,豪华五房
compose操作符
场景一:
我们可以用 compose 操作符来进行线程的切换,一般用在网络请求的地方。
原始的写法为:
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
封窗成工具类
Java版本:
public class RxThreadUtils {
/**
* Flowable 切换到主线程
*/
public static <T> FlowableTransformer<T, T> flowableToMain() {
return upstream -> upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* Observable 切换到主线程
*/
public static <T> ObservableTransformer<T, T> observableToMain() {
return upstream -> upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* Maybe 切换到主线程
*/
public static <T> MaybeTransformer<T, T> maybeToMain() {
return upstream -> upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
Kotlin版本:
object RxThreadUtils {
/**
* Observable 切换到主线程
*/
fun <T> observableToMain(): ObservableTransformer<T, T> {
return ObservableTransformer { upstream ->
upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
}
/**
* Flowable 切换到主线程
*/
fun <T> flowableToMain(): FlowableTransformer<T, T> {
return FlowableTransformer { upstream ->
upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
}
/**
* Maybe 切换到主线程
*/
fun <T> maybeToMain(): MaybeTransformer<T, T> {
return MaybeTransformer { upstream ->
upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
}
}
优化调用:
.compose(RxThreadUtils.flowableToMain())
使用场景二
compose 操作符可以和 Transformer 结合使用,一方面可以让代码看起来更加简洁,另一方面能够提高代码的复用性,因为 RxJava 提倡链式调用,我们可以合理的使用 compose 操作符来防止链式调用被打破。
RxLifecycle 是 trello 开源的一个配置 RxJava 使用的开源库,我们知道 RxJava 有个缺点就是会导致内存泄露,此时,RxLifecycle 横空出世了,它可以配合 RxJava 一起使用,可以有效防止内存泄漏发生,使用起来也是非常方便,举个简单的例子:
myObservable
.compose(RxLifecycle.bind(lifecycle))
.subscribe();
Github
知乎开源
///背压相关///
如果把rxjava比作水管,相信很多东西也是很好理解的,如果没有切换线程 ,
水管的上游跟下游会是一根水管,上游发送多少事件,下游就处理多少事件,
不会产生阻塞,但是如果切换了线程,因为上下游不再一根水管里,如果下游的的水管不知道处理什么原因流的慢了(比如处理一些耗时操作)上游发送的数据因为来不及处理就是越积越多,最终内存爆掉了,那么上游发送的数据都存到什么地方去了呢,可以理解为有一个瓮来保存这些数据,但是因为这个我瓮是个固定大小,所以当达到他的存储上限之后,这个瓮就爆炸了,
根据这个思路,我们防止这个瓮被爆掉有如下几种方式:
一是从数量上进行治理, 减少发送进水缸里的事件
二是从速度上进行治理, 减缓事件发送进水缸的速度
//1.通过filter操作符过滤发送事件,达到控量的目的
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
for (int i = 0; ; i++) {
emitter.onNext(i);
}
}
}).subscribeOn(Schedulers.io())
.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) throws Exception {
return integer % 10 == 0;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "" + integer);
}
});
//2.0sample操作符, 这个操作符每隔指定的时间就从上游中取出一个事件发送给下游
//虽然上游仍然一直在不停的发事件, 但是我们只是每隔一定时间取一个放进瓮里, 并没有全部放进瓮里
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
for (int i = 0; ; i++) {
emitter.onNext(i);
}
}
}).subscribeOn(Schedulers.io())
.sample(2, TimeUnit.SECONDS) //sample取样
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "" + integer);
}
});
这两种方式最大的缺点就是会丢失大部分事件,如果我们既不想丢失事件又不想让内存爆掉的话,我们可以控制上游发送事件的速度
比方说对上游发送事件做一个延时处理
还有一种方式用来解决内存溢出的问题,就是背压
所谓背压就是:
上游变成了Flowable, 下游变成了Subscriber, 但是水管之间的连接还是通过subscribe()
他有四种处理策略:
BackpressureStrategy.BUFFER 大水缸
BackpressureStrategy.ERROR 128的水缸
BackpressureStrategy.DROP 直接把消费不了的事件丢弃
BackpressureStrategy.LATEST 就是只保留最新的事件
//链式调用
Flowable.create((FlowableOnSubscribe<Integer>) emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
//这种方式会在出现上下游流速不均衡的时候直接抛出一个异常,
//这个异常就是著名的MissingBackpressureException
},BackpressureStrategy.ERROR).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
//一定要设置 request当做是一种能力, 当成Subscriber处理事件的能力
//在Flowable里默认有一个大小为128的水缸, 当上下游工作在不同的线程中时,
//上游就会先把事件发送到这个水缸中, 因此, 下游虽然没有调用request,
//但是上游在水缸中保存着这些事件, 只有当下游调用request时, 才从水缸里取出事件发给下游.
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
System.out.println("onNext---" + integer);
}
@Override
public void onError(Throwable t) {
System.out.println("onError---" + t.getMessage());
}
@Override
public void onComplete() {
System.out.println("onComplete---");
}
});
----------------------------分割线---------------------------------
Flowable.interval(1, TimeUnit.MICROSECONDS)
.onBackpressureDrop() //加上背压策略
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Long>() {
@Override
public void onSubscribe(Subscription s) {
s.request(128);
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Long aLong) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
}
});
和Observable功能几乎一模一样,★★背压策略,一定要在线程变换之前去调用★★
//区别在于:
//1.定义的类功能不一样
//2.Flowable可以通过Subscription对象,调用request(n),响应式拉取数据,来支持背压特性 //io.reactivex.Flowable
//3.★★背压策略,一定要在线程变换之前去调用★★
除了这两种还有以下几种:
//基本都使用他
//io.reactivex.Observable
//这个流只会收到一个数据或者一个error,也就是要不然执行onSuccess要不然就执行onError
//io.reactivex.single
//和Single类似正常流程也是只执行onSuccess,但在出现错误的时候,可以选择是执行onError还是onComplete
//io.reactivex.Maybe
//这个流没有数据,只会收到error或者complete
//io.reactivex.Completable
参考链接:
https://www.jianshu.com/p/1922bd75ee73
相关博客
https://www.jianshu.com/p/cd3557b1a474
该系列有十篇
https://www.jianshu.com/p/464fa025229e
↓↓↓↓
https://www.jianshu.com/u/c50b715ccaeb