RxJava2.0的使用-3

RxJava2.0的使用-3

十五、Flowable

问题描述:RxJava会开辟一个缓存区来保存已经发送但还未接收的事件。假设observable1发射事件的速度很快,并且事件量足够大,observable2仅发射仅有的事件,使用zip操作符时,缓存区(采用FIFO)的占用大小会以指数增长。

Memory的Allocated的使用情况是下图这样的,最终导致OOM。

解决这个流速不均匀的问题可以从两方面着手:

  1. 从数量上着手,减少缓存的事件数量,例如使用 sample 操作符定时发射事件。但会导致事件丢失。
  2. 从速度上着手,减缓发射到缓存里事件的速度。但会导致性能下降。

工作线程问题:

  1. 同步:当Observable和Observer处于同一线程时,Observable每发射一个事件,Observer就会处理一个事件,当Observer处理完当前事件后,Observable才会发送下一事件,所以内存使用是平缓状态。
  2. 异步:当Observable和Observer处于不同线程时,它们在各自线程完成发射事件/接收事件工作,此时缓存区占用大小会以指数增长,最终导致OOM。

这跟Flowable有什么关系呢?来看下Flowable的作用:Flowable在采用响应式拉取思路设计实现的,即数据接收源有能力接收多少事件,数据发射源就拿出多少事件(详见下文)。

例子描述:发射5个事件,指定接收3个事件。


使用该字段参数,当发射事件与接收事件流速不均衡时会抛MissingBackpressureException异常。

Flowable的几种背压策略:
1. BackpressureStrategy.ERROR:缓存区默人大小128,流速不均衡时发射MissingBackpressureException信号。
2. BackpressureStrategy.BUFFER:缓存区不限制大小,使用不当仍会OOM。
3. BackpressureStrategy.DROP:缓存最近的nNext事件。
4. BackpressureStrategy.LATEST:缓存区会保留最后的OnNext事件,覆盖之前缓存的OnNext事件。
5. BackpressureStrategy.MISSING:OnNext事件没有任何缓存和丢弃,下游要处理任何溢出。


由Observer变成了Subscriber,主要区别是增加了void request(long n)方法。

备注:指定接收所有事件时可以使用如下代码:

s.request(Long.MAX_VALUE);

由Log可以看出,Subscriber确实接收了3个事件,但是紧着出现error。这是为什么呢?原因在于Flowable与Subscriber工作在同一线程,既然Subscriber没有能力接收剩余事件,Flowable不能一直处于等待状态,所以抛出异常提示。

当Flowable与Subscriber工作在不同线程时:

当Flowable与Subscriber工作在不同线程时,Flowable默认有一个大小为128为缓存区,Flowable会先把事件发送至这个缓存区中,当Subscriber调用request()方法时,才从缓存区中把事件取出来发给Subscriber处理。当Subscriber没有处理事件的能力,缓存区不够用时,依然会抛异常。即Flowable一开始发送了所有事件,只不过将事件先放置在缓存区,等到Subscriber有能力处理时,再从缓存区把事件拿出来发送出去。

那么,Flowable怎么知道Subscriber有能力处理多少事件呢?

来看下FlowableEmitter的源码:

public interface FlowableEmitter<T> extends Emitter<T> {

    void setDisposable(@Nullable Disposable s);

    void setCancellable(@Nullable Cancellable c);

    /**
     * The current outstanding request amount.
     * <p>This method is thread-safe.
     * @return the current outstanding request amount
     */
    long requested();

    boolean isCancelled();

    @NonNull
    FlowableEmitter<T> serialize();
}

这个注释的意思是:当前未完成的请求数量。

例子描述:将请求事件和发射事件都改为手动调用。

当Flowable与Subscriber工作在同一线程时:







由Log可以看到,一开始没有调用request()方法,此时current requested 为 0;当调用request(10)方法后,current requested 为 10;当发射了 5 个事件后,current requested 为 5;又发射了 5 个事件后,current requested 为 0;当调用request(10)方法后,current requested 更改为 10;调用request(10)方法后,current requested 为更改 20。

由此可以看出,当调用request()方法时,Flowable就知道Subscriber有能力处理多少事件了,就开始发送事件,current requested随着发送事件递减;多次调用request()方法,current requested会叠加。在BackpressureStrategy.ERROR背压模式下,当current requested 为 0 时继续发送事件,会抛MissingBackpressureException。

当Flowable与Subscriber工作在不同线程时:

由Log可以看出,一开始没有调用request()方法时,current requested 为 128,此时 current requested 的值并不指Subscriber有多少处理事件的能力,而是指缓存区当前容量为128;当调用request(95)后,current requested 的值并没有增加,因为在BackpressureStrategy.ERROR背压模式下缓存区的大小时固定的;当发射127个事件后,current requested 的值减为 1 ,此时Subscriber应该是处理了95个事件,current requested 却没有重置;当调用request(1)后,current requested 重置为97;调用request(95)后,current requested 依然为97;发射95个事件后,current requested 重置为98;再次发射95个事件后,current requested 减为 3 ;调用request(96)后,current requested 重置为99。

由此可以看出当Flowable与Subscriber工作在不同线程时:
1. current requested是指缓存区当前容量大小;
2. 当Subscriber真实消耗96个事件后,current requested 会被重置。即光发射事件不处理,或光调用request()不发射事件都不会重置 current requested 。
3. 在BackpressureStrategy.ERROR背压模式下,当发射事件数 大于 current requested,依然会抛异常。

current requested用途: 发送事件前判断current requested 的值,若(current requested == 0)说明数据接收源处理不过来了,需要做相应处理。

说了一堆废话,哈哈O(∩_∩)O哈哈~。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值