前言
契机是最近准备上手Flow了,想回顾一下Rxjava。
一图流
补充1:图示单词意思翻译
Observable 可观测量;源
Observer 观察者
Subject 主题
Subscriber 订阅者
对照图,我们知道可以被观察变化的是各种数据源Source和出版者Publisher,而观察变化的是各种Observer 观察者和Subscriber 订阅者
事件订阅我们可以类比成一根水管的上下游,值得关心的地方有很多,比如上游发送到顺序下游接收顺序、水滴在流动中途的变化、水滴的大小等。配合操作符进行理解。
补充2:观察者和发布订阅一览
观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。
- 观察者模式,面向接口编程,实现松耦合。
- 发布订阅模式里,发布者和订阅者,不是松耦合,而是完全解耦的。(发布者和订阅者都不知道对方的存在)
因为有调度中心的存在,所以发布订阅模式一般来说是异步的,使用消息队列实现。而观察者模式,一般来说是同步的,数据源发生变化立即调用某某接口方法。
补充3:接口Disposable
Disposable意思是一次性的。
RxJava 2 引入了Disposables的概念。RxJava 提供Disposable管理订阅的功能。CompositeDisposable和SerialDisposable实现了Disposable。
一次性是Observer和之间的流/链接/连接Observable。它们旨在赋予Observers控制权Observables。在大多数情况下,Observer是一个 UI 元素,即;Fragment、Activity或这些元素的相应视图模型。WhileObservable表示一种数据流,其中数据可以从一个源传递到另一个源,并在需要时在此过程中进行修改。
有时,该数据流可能是长时间运行的 api 请求,或者可能正在侦听数据库中的更新,当您收到响应或发出值时 - 发出请求的屏幕可能已经关闭。在这种情况下,您最终可能会出现内存泄漏,甚至崩溃——这取决于您的实现。
因此,为了避免这些问题,重要的是知道 Android 生命周期的 ui 元素在不再需要它们的数据流时处理它们,即使尚未发出响应也是如此。这Disposable就是派上用场的地方。
Disposable本身自带的接口dispose()和isDisposed()功能。在 RxJava 1 中有一个Subscription具有相同功能的接口。RxJava 3 引入了额外的操作。
随着应用程序的发展和壮大,可能需要从多个来源获取数据。例如,Instagram Feed 屏幕有一个获取帖子列表的端点和另一个获取故事列表的端点(您可以注意到这两个部分如何异步更新并且不相互依赖😉)。附加数据流的列表可大可小。
RxJava 2 没有为这些数据流声明和初始化每个一次性,而是引入了一个CompositeDisposable类来捆绑多个Disposables可以一次全部处理的类。它基本上是包含, ,操作的集合disposables并提供 O(1) 时间复杂度的类。
CompositeDisposable常用的api:
- add(Disposable)
- remove(Disposable)
- delete(Disposable)
- dispose() 此方法只是用于切断,上游还是可以继续发送事件的。
- clear()
使用场景,对一组请求进行监听,中途可以添加和移除,即开启新请求的时候可以取消之前的请求,移除之前的请求监听。
private val compositeDisposable by lazy {
CompositeDisposable()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
compositeDisposable.add(
api.getPosts()
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ posts ->
println(posts)
}, { throwable ->
println(throwable)
})
)
compositeDisposable.add(
api.getStories()
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ stories ->
println(stories)
}, { throwable ->
println(throwable)
})
)
}
override fun onDestroyView() {
compositeDisposable.clear()
super.onDestroyView()
}
补充4:Observables的冷和热
Cold observables 在订阅时开始运行,即,observable 序列仅在 Subscribe 被调用时才开始向观察者推送值。订阅者之间也不共享值。
Hot observables在订阅激活之前就已经产生了值。当观察者订阅一个hot observable时,它会获取订阅后发出的流中的所有值。hot observable 序列在所有订阅者之间共享,每个订阅者被推送到序列中的下一个值。
默认情况下,observable是冷的,使用publish运算符将先前的冷可观察序列源转换为热可观察序列源,该运算符返回一个ConnectableObservable。
所谓链式操作由来
一开始是这样的
//创建一个上游 Observable:
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
});
//创建一个下游 Observer
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Integer value) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
};
//建立连接
observable.subscribe(observer);
简化一下就变成了Rx引以为傲的链式结构
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
}).subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Integer value) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
ObservableEmitter: Emitter是发射器的意思,但是也不能乱发,需要满足一定的规则:
- 上游可以发送无限个onNext, 下游也可以接收无限个onNext.
- 当上游发送了一个onComplete后, 上游onComplete之后的事件将会继续发送, 而下游收到onComplete事件之后将不再继续接收事件.
- 当上游发送了一个onError后, 上游onError之后的事件将继续发送, 而下游收到onError事件之后将不再继续接收事件.
- 上游可以不发送onComplete或onError.
- onComplete和onError必须唯一并且互斥, 即不能发多个onComplete, 也不能发多个onError, 也不能先发一个onComplete, 然后再发一个onError, 反之亦然。
简单示例,以登录注册为例
public interface Api {
@GET
Observable<LoginResponse> login(@Body LoginRequest request);
@GET
Observable<RegisterResponse> register(@Body RegisterRequest request);
}
伪代码
api.register(new RegisterRequest()) //发起注册请求
.subscribeOn(Schedulers.io()) //在IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求注册结果
.doOnNext(new Consumer<RegisterResponse>() {
@Override
public void accept(RegisterResponse registerResponse) throws Exception {
//先根据注册的响应结果去做一些操作
}
})
.observeOn(Schedulers.io()) //回到IO线程去发起登录请求
.flatMap(new Function<RegisterResponse, ObservableSource<LoginResponse>>() {
@Override
public ObservableSource<LoginResponse> apply(RegisterResponse registerResponse) throws Exception {
return api.login(new LoginRequest());
}
})
.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求登录的结果
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse loginResponse) throws Exception {
Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
}
});
常用操作符
操作符才是Rx的精髓所在,Rx是真的强。
map
用于改变数据,返回一个 Observable,将指定的函数应用于源 ObservableSource 发出的每个项目,并发出这些函数应用程序的结果,不管你的函数长啥样,查看转换的函数泛型<T, R>,前面是传入的,后面的是改变后的。
flatmap
某Observable变换成多个发送事件的Observable发送给下游,不保证顺序。
concatMap
与flatmap作用一样,严格按顺序发送。
fromIterable和fromArray
将数组和可迭代对象转换为可观察序列
take
一个 Observable 只发出源 ObservableSource 发出的第一个count项,count为要发出的最大项目数。简单点理解就是我需要获取几次。
zip
通过一个函数将多个Observable合并到一起,它只发射与发射数据项最少的那一个Observable一样多的数据,组合顺序严格按发送顺序。
timer
指在一段时间延迟后发送0L并完成,返回Observable ,常用于实现定时器。
interval
interval(long initialDelay, long period, TimeUnit unit, Scheduler scheduler)用的比较多,返回一个 Observable,先延时一段时间后发送一次,然后每过一段时间发送一次。
sample
如果有最近发出点事件,那sample就是用来取出事件发送的,可以指定延时。
publish
冷流转热流。
Flowable和Backpressure
Flowable设计时采用响应式拉取式,用于解决上下游流速不均衡问题。把request当做一种能力,也就是下游处理事件的能力。
不同于Observable,Flowable 创建时多了个参数背压(Backpressure)
- BackpressureStrategy.ERROR 默认128
- BackpressureStrategy.BUFFER 大小无限制
- BackpressureStrategy.DROP 存不下丢弃(快满的时候)
- BackpressureStrategy.LATEST 只存最新的(同上)
使用Flowable,观察者Observer换成订阅者Subscriber,OnSubscribe方法传给我们不再是Disposable,而是Subscription。订阅者有一个cancel方法可以像Disposable.dispose()一样去切断。值得一提的是,Observable里有一个toFlowable方法,可以直接转成Flowable。
零碎知识
- FlowableEmitter的request()获取当前未完成的请求总量,此方法是线程安全的。
- 上游调用emitter.requested()获取requested值后,上游使用emitter调用onnext一次,requested做一次减一;下游调用一次request就一次加法(还要注意上下游在不同的线程里时,每个线程都有一个requested,所以下游调用s.request改变的是下游所在线程中的requested,什么都不做就是默认的128)
- 上游发送完128个之后就停了,等待下游告诉他消费情况,下游每消费96个事件便会自动触发request(int x)设置上游requested值,然后上游又开始发送。(这个96也是默认的)
示例:读取一个很大的文本 test.txt
public static void main(String[] args) {
practice1();
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void practice1() {
Flowable
.create(new FlowableOnSubscribe<String>() {
@Override
public void subscribe(FlowableEmitter<String> emitter) throws Exception {
try {
FileReader reader = new FileReader("test.txt");
BufferedReader br = new BufferedReader(reader);
String str;
while ((str = br.readLine()) != null && !emitter.isCancelled()) {
while (emitter.requested() == 0) {
if (emitter.isCancelled()) {
break;
}
}
emitter.onNext(str);
}
br.close();
reader.close();
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<String>() {
@Override
public void onSubscribe(Subscription s) {
mSubscription = s;
s.request(1);
}
@Override
public void onNext(String string) {
System.out.println(string);
try {
Thread.sleep(2000);
mSubscription.request(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable t) {
System.out.println(t);
}
@Override
public void onComplete() {
}
});
}
后话
准备看下Flow了,貌似还不错。