深入浅出聊RxJava合集(Kotlin版)第二篇

Rxjava的设计模式

  • 观察者模式
  • 装饰者模式

Rxjava的核心内容

  • Rxjava背压策略
  • Rxjava线程切换
  • Rxjava生命周期管理

Rxjava背压策略

什么是背压

当上下游在不同的线程中,通过Observable发射,处理,响应数据流时,如果上游发射数据的速度快于下游接收处理的速度,导致没来得及处理的数据就会造成积压,这些数据既不会丢失,也不会被GC,而是存在一个异步缓存池中,如果缓存池的数据一直得不到处理,越积越多,最后就会造成内存OOM,这边是响应式编程的背压问题。使用白话来举例子:好比不停的往水池里面加水,水池也在出水,但是出水比进水慢的多,最后会导致水池爆满溢出。

背压策略的解决思路

通过响应式拉取,即观察者主动去被观察者那拉取事件,而被观察者发送的事件则加入异步缓存池等待被拉取,说白了就是一个被动接受,一个是主动拉取。

例子
无背压策略的情况:
在这里插入图片描述
如图所示,蓝色的框框就是zip组合操作符给我们的‘水池’。它将每根水管发出的事件保存起来,等两个水池都有事件之后就分别从水池中取出一个事件来组合,当其中一个水池为空的时候就处于等待状态。

        // 在RxJava2.0开始,Observable不再支持背压。
        val observable1 = Observable.create<Int> { emitter ->
        var index = 0;
            while (true) { //无限发送事件,每100ms发送一个事件
                Log.i(TAG, "observable2 emitter $index")
                emitter.onNext(index)
                Thread.sleep(100)
                index++
            }
        }.subscribeOn(Schedulers.newThread())
        
        val observable2 = Observable.create<String> { emitter ->

            while (true) { //无限发送事件,每1000ms发送一个事件
                Log.i(TAG, "observable2 emitter A")
                emitter.onNext("A")
                Thread.sleep(1000)
            }
        }.subscribeOn(Schedulers.newThread())
        
        Observable.zip(observable1, observable2, object : BiFunction<Int, String, String> {
            override fun apply(t1: Int, t2: String): String {
                // observable1发送事件的速度大于observable2
                // 当observable2发送事件时observable1才会立即先从“水池”中取出事件与其相组合
                val content = t2.plus(t1)
                Log.i(TAG, "zip apply:$content")
                return content
            }
        }).subscribeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Consumer<String>{
                    override fun accept(s: String?) {
                        Log.i(TAG,"accept:$s")
                    }
                },object :Consumer<Throwable>{
                    override fun accept(t: Throwable?) {
                        Log.i(TAG,"throwable:$t")
                    }
                })

运行结果

2021-01-15 16:38:48.740 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 0
2021-01-15 16:38:48.740 12646-12688/com.example.rxjavatest I/lxl: observable2 emitter A
2021-01-15 16:38:48.740 12646-12688/com.example.rxjavatest I/lxl: zip apply:A0
2021-01-15 16:38:48.741 12646-12688/com.example.rxjavatest I/lxl: accept:A0
2021-01-15 16:38:48.841 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 1
2021-01-15 16:38:48.941 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 2
2021-01-15 16:38:49.041 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 3
2021-01-15 16:38:49.142 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 4
2021-01-15 16:38:49.242 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 5
2021-01-15 16:38:49.343 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 6
2021-01-15 16:38:49.443 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 7
2021-01-15 16:38:49.544 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 8
2021-01-15 16:38:49.644 12646-12687/com.example.rxjavatest I/lxl: observable1 emitter 9
2021-01-15 16:38:49.741 12646-12688/com.example.rxjavatest I/lxl: observable2 emitter A
2021-01-15 16:38:49.741 12646-12688/com.example.rxjavatest I/lxl: zip apply:A1
2021-01-15 16:38:49.741 12646-12688/com.example.rxjavatest I/lxl: accept:A1

代码运行结果解读:

只有observable2发送事件后,zip才会进行组合事件,并发射的下游,当observable2无事件的时候,是处于等待状态的。

observable1和observable2好比两根水管,分别处于不同的线程中发送事件,但是observable2发送事件的速度明显要小于observable1的速度。根据zip组合操作符的特性(最终发送的事件数量会与源Observable中最少事件的数量保持一致(也就是必须配对才会发送事件出去)。也就是说只有在observable2发送事件后,observable1才会立即从缓存的事件中取出事件与其组合,发送给下游接收。如果任由observable1发送事件而不及时处理,最终“水池”中存放的事件会越来越多,最终OOM。

类似这种,任何上游发送的事件下游来不及处理,都可能会导致这种情况的发生。

解决想法

  • 控制上游事件的发送速度和数量
  • 缓存事件池增加手动开关,由自动控制变为主动控制

RxJava提供的解决方案 - Flowable/Subscriber,响应式拉取来主动控制释放缓存池中的事件数量来达到平衡。

Flowable-Subscriber

 Flowable.create(object : FlowableOnSubscribe<Int> {
            override fun subscribe(emitter: FlowableEmitter<Int>) {
                // 发送事件
                for (index in 0..5) {
                    Log.i(TAG, "Flowable emitter $index")
                    emitter.onNext(index)
                }
            }
        }, BackpressureStrategy.MISSING)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Subscriber<Int> {
                    override fun onSubscribe(s: Subscription?) {
                        Log.i(TAG, "Subscription")
                        s?.let {
                           mSubscription = it
                        }
                    }

                    override fun onNext(t: Int?) {
                        Log.i(TAG, "onNext:$t")
                    }

                    override fun onError(t: Throwable?) {
                        Log.i(TAG, "onError:$t")
                    }

                    override fun onComplete() {
                        Log.i(TAG, "onComplete")
                    }
                })

看上面这段代码,我们可以发现Flowable用法其实与Observable/Observer基本一样,区别就是发射器ObservableEmitter替换成了FlowableEmitter,Disposable替换成了Subscription,增加了一个参数背压模式BackpressureStrategy.MISSING (共5种后面会提到)。
运行结果

2021-01-15 16:48:23.200 14463-14463/com.example.rxjavatest I/lxl: onSubscribe
2021-01-15 16:48:23.201 14463-14515/com.example.rxjavatest I/lxl: Flowable emitter 0
2021-01-15 16:48:23.201 14463-14515/com.example.rxjavatest I/lxl: Flowable emitter 1
2021-01-15 16:48:23.201 14463-14515/com.example.rxjavatest I/lxl: Flowable emitter 2
2021-01-15 16:48:23.201 14463-14515/com.example.rxjavatest I/lxl: Flowable emitter 3
2021-01-15 16:48:23.201 14463-14515/com.example.rxjavatest I/lxl: Flowable emitter 4
2021-01-15 16:48:23.201 14463-14515/com.example.rxjavatest I/lxl: Flowable emitter 5

看打印结果可知,我们只看到了上游发送的日志,下游没有接收到?是因为上游发送的事件会被保存缓存中“水池”,等待下游接收。

 //控制开关“放水”:由自己手动控制是否接收事件
 //int参数指定取出事件数量
 mSubscription?.request(1)

运行结果

2021-01-15 16:57:52.063 15501-15501/com.example.rxjavatest I/lxl: onSubscribe
2021-01-15 16:57:52.065 15501-15541/com.example.rxjavatest I/lxl: Flowable emitter 0
2021-01-15 16:57:52.065 15501-15541/com.example.rxjavatest I/lxl: Flowable emitter 1
2021-01-15 16:57:52.065 15501-15541/com.example.rxjavatest I/lxl: Flowable emitter 2
2021-01-15 16:57:52.065 15501-15541/com.example.rxjavatest I/lxl: Flowable emitter 3
2021-01-15 16:57:52.065 15501-15541/com.example.rxjavatest I/lxl: Flowable emitter 4
2021-01-15 16:57:52.065 15501-15541/com.example.rxjavatest I/lxl: Flowable emitter 5
2021-01-15 16:57:52.083 15501-15501/com.example.rxjavatest I/lxl: onNext:0
2021-01-15 16:57:52.083 15501-15501/com.example.rxjavatest I/lxl: onNext:1

通过Subscription.request(n)方法拉取数据,当所有事件被取出后才会调用onComplete()。注意onComplete() 和 OnError()不占用事件个数。

Observable和Flowable两种模式的数据传输的区别:

  • Observable/Oberver,上游发送数据放到缓存区,下游自动从缓存获取数据,中间没有开关控制,如果上游发送数据的速率超过了下游接收处理数据的速率,那么缓存区的数据就会不断增大 (缓存大小默认最大缓存),直到OOM。
  • Flowable/Subscriber,上游发送数据放到缓存区,下游不会自动从缓存区获取数据,中间有开关控制。Subscription.request(n),控制接收数据的个数。如果上游一直发送事件,而下游不接受,那么缓存区会怎么样呢?这就是下面我们要讲的了 - 背压策略

背压策略

public abstract class Flowable<T> implements Publisher<T> {
    /** The default buffer size. */
    static final int BUFFER_SIZE;
    static {
        BUFFER_SIZE = Math.max(1, Integer.getInteger("rx2.buffer-size", 128));
    }

从Flowable的源码中我们可以找到,默认缓存大小为128
BackpressureStrategy,共有5种模式:

  • MISSING 写入OnNext事件时不会进行任何缓冲或丢弃,下游必须处理任何溢出。当超过缓存区大小的时候,会自动发送一个onError事件 (onError和onComplete不占用事件数),即MissingBackpressureException: Queue is full?! 。
  • ERROR和MISSING相似 (在使用效果上),也会自动发送一个onError事件,不同的是错误信息,MissingBackpressureException: create: could not emit value due to lack of requests 。
  • BUFFER缓存区大小没有限制,也就是上游发送的事件在下游没有接收,会一直在缓存区里,直到缓存溢出即OOM (跟Observable不同的是Flowable是响应式拉取)。
  • DROP当超过缓存区大小的时候,后续发送的事件就会被丢弃。直到缓存区有事件被取出,新发送的事件才能继续加入到缓存池中。
  • LATEST和DROP类似,当超过缓存区大小的时候,后续发送的事件就会被丢弃。区别在于超出缓存区大小后,会强行加入一条最新事件 (这个事件不占用缓存池大小),并且会不断被新的事件所覆盖。确保了在调用onComplete()之前,一定能获取到最后发送的事件。

Flowable使用操作符Flowable中有些创建操作符不能指定背压策略模式时:

操作符对应的背压策略
onBackpressureBuffer()BackpressureStrategy.BUFFER
onBackpressureDrop()BackpressureStrategy.DROP
onBackpressureLatest()BackpressureStrategy.LATEST

Rxjava线程切换

Rxjava线程切换主要是使用这2个操作符:

  • subscribeOn
    通过接收一个Schedule参数,来指定对数据的处理运行在特定的线程调度器Schedule上,如多次使用则只有一次起作用

使用subscribeOn操作符指定被观察者发送事件的线程

observable.subscribeOn(Schedulers.io())
  • observeOn
    接收一个Scheduler参数,来指定下游操作运行在特定的线程调度器Scheduler上。若多次设定,每次均起作用。

使用observeOn操作符指定观察者处理事件线程:

observable.observeOn(AndroidSchedulers.mainThread()) 

在每次都需要切换线程的时候,都需要写以上代码,所以我们可以使用compose操作符。

ObservableTransformer<Upstream, Downstream> , 是对上游Observable转换成新的Observable的接口,需自己实现apply()方法:

 fun <T> applySchedulers() = ObservableTransformer<T, T> {
        //ObservableTransformer操作符不同于Function
        // 它支持的是对原始的Observable进行操作,可转换成新的Observable
        // 配合compose操作符来实现一些公用的逻辑处理
        it.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
    }

使用compose操作符加ObservableTransformer来复用线程切换操作:

Observable.create<Int> { emitter ->
            // 2.在复写的subscribe()里定义需要发送的事件
            emitter.onNext(1)
            emitter.onNext(2)
            emitter.onNext(3)
            // 3.发送事件完成。
            emitter.onComplete()
        }.flatMap(object : Function<Int, Observable<String>> {
            override fun apply(t: Int): Observable<String> {
                return Observable.just("flatMap:$t")
            }
        }).compose(applySchedulers())
                .subscribe(object : Observer<String> {
                    override fun onSubscribe(d: Disposable) {
                        Log.d(TAG, "开始采用subscribe连接")
                    }

                    override fun onNext(t: String) {
                        Log.d(TAG, "对Next事件" + t + "作出响应")
                    }

                    override fun onError(e: Throwable) {
                        Log.d(TAG, "对Error事件作出响应")
                    }

                    override fun onComplete() {
                        Log.d(TAG, "对Complete事件作出响应")
                    }
                })

Schedulers类:

public final class Schedulers {
	// 此款调度器非常简单,由一个线程支持。所以无论有多少个observables,都将只运行在这个线程上。
	// 也可将其认为是主线程的一个替代。
    @NonNull
    static final Scheduler SINGLE;
	// 这个调度器和IO调度器非常相似,因其也是由线程池支持的。
	// 可用的线程数是固定的,和系统的cpu核数目保持一致。
	// 所以如果你的手机是双核的,那么线程池中就有两个线程。这也意味着如果这两条线程都处于忙碌状态,
	// 那么该进程将会等待线程变成空闲状态的时候才能处理其他操作。因为这个限制,它不太适合IO相关操作。
	// 适用于进行一些计算操作,计算速度还很快。
    @NonNull
    static final Scheduler COMPUTATION;
	// 最常见的调度器之一。它们用于IO相关操作。比如网络请求和文件操作。IO调度器背后由线程池支撑。
	// 它首先创建一个工作线程,可以复用于其他操作。
	// 当然,当这个工作线程(长时间任务的情况)不能被复用时,会创建一个新的线程来处理其他操作。
	// 这个好处也会带来一些问题,因为允许创建的线程是无限的,所以当创建大量线程的时候,会对整体性能造成一些影响。
    @NonNull
    static final Scheduler IO;
	// 此调度器运行在当前线程,所以如果你有代码运行在主线程上,它会将将要运行的代码块添加到主线程的队列。
	// Trampoline不会阻塞此线程,它会等待当前任务执行完成。
	// 适用于不止一个observables,并且希望他们能够按照顺序执行的场景。
    @NonNull
    static final Scheduler TRAMPOLINE;
	// 此调度器会在当前线程开启一个新的子线程,相当于newThread()操作。
    @NonNull
    static final Scheduler NEW_THREAD;
    
	...
}

AndroidSchedulers类:

由RxAndroid库提供,使用AndroidSchedulers.mainThread()获取一个持有主线程Looper的Handler的HandlerScheduler。

常用于将操作切换到主线程以便操作UI,经常用于observeOn方法。

public final class AndroidSchedulers {

    private static final class MainHolder {
    	// 创建持有主线程Looper的Handler,传入给HandlerScheduler
        static final Scheduler DEFAULT = new HandlerScheduler(new Handler(Looper.getMainLooper()), false);
    }

    private static final Scheduler MAIN_THREAD = RxAndroidPlugins.initMainThreadScheduler(
            new Callable<Scheduler>() {
                @Override public Scheduler call() throws Exception {
                    return MainHolder.DEFAULT;
                }
            });

    /** A {@link Scheduler} which executes actions on the Android main thread. */
    public static Scheduler mainThread() {
        return RxAndroidPlugins.onMainThreadScheduler(MAIN_THREAD);
    }
    ...... 
}

Rxjava线程切换原理

具体的讲解见深入浅出聊RxJava合集(Kotlin版)第三篇Rxjava原理剖析

Rxjava生命周期管理

Rxjava是一个异步的库,很容易就会造成内存泄漏,这样就需要进行生命周期的管理,我们的做法是:

  Observable.interval(0, 1, TimeUnit.SECONDS)
                .flatMap(object : Function<Long, Observable<String>> {
            override fun apply(t: Long): Observable<String> {
                return Observable.just("flatMap:$t")
            }
        }).compose(applySchedulers())
                .subscribe(object : Observer<String> {
                    override fun onSubscribe(d: Disposable) {
                        Log.d(TAG, "开始采用subscribe连接")
                        disposable = d
                    }

                    override fun onNext(t: String) {
                        Log.d(TAG, "对Next事件" + t + "作出响应")
                    }

                    override fun onError(e: Throwable) {
                        Log.d(TAG, "对Error事件作出响应")
                    }

                    override fun onComplete() {
                        Log.d(TAG, "对Complete事件作出响应")
                    }
                })


    override fun onDestroy() {
        super.onDestroy()
        // 在Activity退出的时候解除订阅,停止发送!
        disposable?.dispose()
    }
2021-01-16 16:06:31.106 31885-31885/com.example.rxjavatest D/lxl: 开始采用subscribe连接
2021-01-16 16:06:31.126 31885-31885/com.example.rxjavatest D/lxl: 对Next事件flatMap:0作出响应
2021-01-16 16:06:32.109 31885-31885/com.example.rxjavatest D/lxl: 对Next事件flatMap:1作出响应
2021-01-16 16:06:33.111 31885-31885/com.example.rxjavatest D/lxl: 对Next事件flatMap:2作出响应
2021-01-16 16:06:34.111 31885-31885/com.example.rxjavatest D/lxl: 对Next事件flatMap:3作出响应
2021-01-16 16:06:35.112 31885-31885/com.example.rxjavatest D/lxl: 对Next事件flatMap:4作出响应
2021-01-16 16:06:36.110 31885-31885/com.example.rxjavatest D/lxl: 对Next事件flatMap:5作出响应
2021-01-16 16:06:36.711 31885-31885/com.example.rxjavatest D/lxl: 对Complete事件作出响应

退出Activity后,取消订阅,会停止发送和接收。

这样的话,每次都要进行这样写吗?不,肯定有替代的,这样我们就引入了RxLife,下面是RxLife的使用。

subscribe之前加入以下代码。

 .compose(bindUntilEvent(ActivityEvent.DESTROY))
 
 // ActivityEvent.DESTROY 表示activity销毁的时候,对应activity的 onDestroy() 什么周期
 /**
 public enum ActivityEvent {
    CREATE, // 对应 onCreate()
    START, // 对应 onStart()
    RESUME, // 对应 onResume()
    PAUSE, // 对应 onPause()
    STOP, // 对应 onStop()
    DESTROY // 对应 onDestroy()
}
 */
	
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有志成为大咖的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值