阅读 1616

Kotlin 协程Flow VS Rxjava2 (二) RxJava2

RxJava2

方便线程切换的一个库,类似于HandlerKotlin的协程,内部维护了一个线程池。

RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。

引入依赖

    implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    api 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'
复制代码

观察者模式

Observable (被观察者),Observer(观察者), subscribe() (建立订阅关系)

其实可以把 RxJava 比喻成一个做果汁,家里有很多种水果(要发送的原始数据),你想榨点水果汁喝一下,这时候你就要想究竟要喝什么水果汁呢?如果你想喝牛油果雪梨柠檬汁,那你就要把这三种水果混在一起榨汁(使用各种操作符变换你想发送给观察者的数据),榨完后,你就可以喝上你想要的果汁了(把处理好的数据发送给观察者)

fun main() {
    // 创建一个被观察者
    Observable.create(object : ObservableOnSubscribe<String> {
        override fun subscribe(e: ObservableEmitter<String>) {
            e.onNext("1")
            e.onNext("2")
            e.onNext("3")
            e.onComplete()
        }
        // 订阅 一个观察者
    }).subscribe(object : Observer<String> {
        override fun onComplete() {
            println("onComplete")
        }

        override fun onSubscribe(d: Disposable) {
            println("onSubscribe")
        }

        override fun onNext(t: String) {
            println("onNext")
        }

        override fun onError(e: Throwable) {
            println("onError")
        }
    })
}
复制代码

创建被观察者Observable

create:同上

justjust发送的事件不可以超过10个

fun main() {
    // 创建一个被观察者
    Observable.just("11", "33", "12").subscribe(object : Observer<String> {
        override fun onComplete() {
            println("onComplete")
        }

        override fun onSubscribe(d: Disposable) {
            println("onSubscribe")
        }

        override fun onNext(t: String) {
            println(t)
        }

        override fun onError(e: Throwable) {
            println("onError")
        }
    })
}
复制代码

fromArray:和just类似,不过fromArray可以传入多于10个的变量,还可以直接传入数组,不过要在前面加*

    val array = arrayOf("1", "3", "2")
    // 注意需要加一个*  把数组传给Java中的...
    Observable.fromArray(*array).subscribe(object : Observer<String> {
        override fun onComplete() {
            println("onComplete")
        }

        override fun onSubscribe(d: Disposable) {
            println("onSubscribe")
        }

        override fun onNext(t: String) {
            println(t)
        }

        override fun onError(e: Throwable) {
            println("onError")
        }
    })
复制代码

fromIterable:直接发送list集合给观察者

val list = listOf<String>("1", "3", "2")
Observable.fromIterable(list)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe ({ print(it) }, {print(it.message)})
复制代码

interval(initialDelay: Long, period: Long, unit: TimeUnit):每隔一定的时间会发送一个事件,这个事件是从0开始,不断增1的数字。initialDelay表示第一次发送事件的延迟时间。比较适合轮询的处理

       val disposable = CompositeDisposable() 
       disposable.add(
                Observable.interval(0, 1, TimeUnit.SECONDS)
                        .retry(3)
                        .delay(4, TimeUnit.SECONDS)
                        .map { it.toString() }
                        .subscribe { (t -> Log.i(TAG, t)), {Log.i(TAG, it.message)} }
       )
复制代码

订阅观察者

    public final Disposable subscribe() {}
    // 表示没有观察者
    public final Disposable subscribe(Consumer<? super T> onNext) {}
    // 表示观察者只对被观察者发送的Next事件作出响应
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {} 
    // 表示观察者只对被观察者发送的Next事件 & Error事件作出响应

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}
    // 表示观察者只对被观察者发送的Next事件、Error事件 & Complete事件作出响应

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}
    // 表示观察者只对被观察者发送的Next事件、Error事件 、Complete事件 & onSubscribe事件作出响应

    public final void subscribe(Observer<? super T> observer) {}
复制代码

通过disposable.dispose() 切断观察者与被观察者之间的连接,但被观察者还是可以继续发送事件

变换操作符

map:可以将被观察者发送的数据类型转变成其他的类型

flatMap:可以将事件序列中的元素进行整合加工,返回一个新的被观察者。无序的

data class Plan(val names: List<String>)
data class Person(val plans: List<Plan>)

fun main() {
    val planList = listOf<Plan>(Plan(listOf("张三", "李四")), Plan(listOf("王五", "ddddd")))
    val personList = listOf<Person>(Person(planList), Person(planList))
    Observable.fromIterable(personList)
        .flatMap { Observable.fromIterable(it.plans) }
        .flatMap { Observable.fromIterable(it.names) }
        .subscribe ({
            println(it)
        }, {})
}
复制代码

concatMap:同flatMap,但是发送的事件是有序的。新合并生成的事件序列顺序是有序的,即 严格按照旧序列发送事件的顺序

合并/组合操作符

concat/concatArray:连接多个被观察者,concatArray无数量限制,concat最多发送四个事件。合并后按发送顺序串行执行,非并发

fun main() {
    Observable.concat(
        Observable.just(1, 2),
        Observable.just(4, 3),
        Observable.just(6, 5)
    ).subscribe ({ println(it) }, {})
}
// 1  2  4  3  6  5
复制代码

merge/mergeArray:类似于concat/concatArray,合并后 按时间线并行执行

private fun initRxDemo() {

   val zip1 = Observable.just(listOf("A", "B", "C", "DDDD"))
   val zip2 = Observable.just(listOf(1, 2, 3))

   disposable.add(
       Observable.merge(zip1, zip2)
                        // List<Any> 类型
                       .concatMap { Observable.fromIterable(it) }
                       .subscribeOn(Schedulers.io())
                       .observeOn(AndroidSchedulers.mainThread())
                       .subscribe({ Log.i(TAG, it.toString()) }, { showError() })
    )
}

复制代码

zip:用于同时发送多个网络请求,合并多个请求结果数据。类似于Kotlin协程的async

  1. 事件组合方式 = 严格按照原先事件序列 进行对位合并
  2. 最终合并的事件数量 = 多个被观察者(Observable)中数量最少的数量
private fun initRxDemo() {
        val zip1 = Observable.fromIterable(listOf("A", "B", "C", "DDDD"))
        val zip2 = Observable.fromIterable(listOf(1, 2, 3))

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            disposable.add(
                    Observable.zip(zip1, zip2, object : BiFunction<String, Int, String> {
                        override fun apply(t: String, u: Int): String {
                            return "$t $u"
                        }
                    })
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe({ Log.i("LIUJIANDD", it.toString()) }, { showError() })
            )
        }
 }
//com.liujian.kotlindemo I/LIUJIANDD: A 1
//com.liujian.kotlindemo I/LIUJIANDD: B 2
//com.liujian.kotlindemo I/LIUJIANDD: C 3
复制代码

reduce:作用同kotlin集合的reduce

**startWith(T item)**用于在源Observable发射的数据前插入另一个数据事件(这些数据会被插入到源Observable发射数据的前面)。比较适合用作发送网络请求前的ShowLoading事件

    private fun initRxDemo() {
        val zip1 = Observable.fromIterable(listOf("A", "B", "C", "DDDD"))

        zip1.startWith("0")
                .startWithArray("1", "3", "2")
                .subscribe { Log.i(TAG, it) }
    }
    // 1  3  2 0 A B C DDD
复制代码

功能操作符

groupBy:分组

onErrorReturn:如果有error事件,把error事件转化成一个对象

.onErrorReturn { Result.getFailData(it.message) }
复制代码

retry:如果出现错误事件,则会重新发送所有事件序列。times 是代表重新发的次数

fun main() {
    Observable.create<String> {
        it.onNext("1")
        it.onError(Exception("404"))
        it.onComplete()
    }.retry(2)
        .subscribe(object : Observer<String> {
            override fun onComplete() {

            }

            override fun onSubscribe(d: Disposable) {
            }

            override fun onNext(t: String) {
                println(t)
            }

            override fun onError(e: Throwable) {
                println(e.message)
            }

        })
}
//  1  1  1  404
复制代码

filter:过滤被观察者发送的事件,如果返回 true 则会发送事件,否则不会发送

skip(count : Int): count代表跳过事件的数量

take(count: Int):获取前count接收的事件的数量

doOnNext:执行Next事件前调用

fun main() {
    Observable.fromIterable(listOf(3, 2, 3, 4, 5, 7, 9, 10))
        .doOnNext {
            if (it > 5) {
                println("doOnNext$it")
            }
        }
        .subscribe({ println(it)}, {showError()})
}
复制代码

doAfterNextdoOnComplete

doOnEach:当Observable 每发送一次数据事件就会调用一次

compose:抽离RxJava链式调用中一些重复的代码

    private fun refreshToken() {
        Observable.just(UserInfoManager.getRefreshToken())
            .flatMap {
                apiService.reFreshToken(mapOf("refreshToken" to it))
            }
            .compose(getResultTransfer())
            .subscribe(object : Observer<RefreshToken> {
                override fun onComplete() {

                }

                override fun onSubscribe(d: Disposable) {
                }

                override fun onNext(t: RefreshToken) {
                    updateToken(t)
                }

                override fun onError(e: Throwable) {
                    Logger.e(TAG, "refreshToken error: ${e.message}")
                }
            })
    }

    fun getResultTransfer() = ObservableTransformer<RefreshToken, RefreshToken> {
        it.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
    }
复制代码

线程调度

subscribeOn:指定被观察者的线程

observeOn:指定观察者的线程,每执行一次就会生效一次

下表总结了 RxJava 中的调度器:

调度器作用
Schedulers.computation( )用于使用计算任务,如事件循环和回调处理
Schedulers.immediate( )当前线程
Schedulers.io( )用于 IO 密集型任务,如果异步阻塞 IO 操作。
Schedulers.newThread( )创建一个新的线程
AndroidSchedulers.mainThread()Android 的 UI 线程,用于操作 UI。

一个例子

异步线程中处理Bitmap,在主线程展示。不用RxJava的写法

new Thread() {
    @Override
    public void run() {
        super.run();
        for (File folder : folders) {
            File[] files = folder.listFiles();
            for (File file : files) {
                if (file.getName().endsWith(".png")) {
                    final Bitmap bitmap = getBitmapFromFile(file);
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            imageCollectorView.addImage(bitmap);
                        }
                    });
                }
            }
        }
    }
}.start();
复制代码

使用RxJava 的处理如下

fun main() {
    val folders = listOf<File>()
    Observable.fromIterable(folders)
        .flatMap { Observable.fromArray(*it.listFiles()) }
        .filter { it.name.endsWith(".png") }
        .map { getBitmapFromFile(it) }
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe ({
            imageCollectorView.addImage(bitmap)
        }, {showError()})
}
复制代码

CompositeDisposable

Rxjava虽然好用,但是总所周知,容易遭层内存泄漏。也就说在订阅了事件后没有及时取阅,导致在activity或者fragment销毁后仍然占用着内存,无法释放。而disposable便是这个订阅事件,可以用来取消订阅。

​ 创建一个BaseActivity,用CompositeDisposable来管理订阅事件disposable,然后在activity销毁的时候,调用compositeDisposable.dispose()就可以切断所有订阅事件,防止内存泄漏。

abstract class BaseActivity : AppCompatActivity() {

    var mDisposable = CompositeDisposable()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        initParameter()
        setContentView(getLayoutResId())
    }

    override fun onDestroy() {
        super.onDestroy()
        // 切断所有订阅事件 防止内存泄露
        if(!mDisposable.isDisposed){
            mDisposable.dispose()
        }
    }
}
复制代码
btnEnsure.setOnClickListener {
      mDisposable.add(viewModel.login(etAccount.text.toString(), 
                                      etPassword.text.toString()))
}
复制代码

背压

背压是用来解决 被观察者 发送事件速度太快,而观察者 来不及接收所有事件,从而导致观察者无法及时处理所有发送过来事件的问题,最终导致缓存区溢出、事件丢失 & OOM

原理:建立缓存区,对超出缓存区大小(默认128)的事件进行丢弃,保留,报错的措施

背压具体实现Flowable。在2.0 中,Observable 不再支持背压

img

Flowable的基础使用非常类似于Observable

        disposable.add(
                Flowable.create<Int>({
                    for (i in (0 until 300)) {
                        it.onNext(i)
                    }
                    it.onComplete()
                    }, BackpressureStrategy.LATEST)
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe({
                            Log.i(TAG, it.toString())
                        }) { Log.e(TAG, it.toString()) }
        )
复制代码

背压策略:

  • BackpressureStrategy.ERROR:直接抛出异常MissingBackpressureException
  • BackpressureStrategy.MISSING:直接抛出异常MissingBackpressureException,提示Queue is full
  • BackpressureStrategy.BUFFER:缓存区大小设置为无限大。要注意内存情况,避免出现OOM
  • BackpressureStrategy.DROP:超过缓存区大小(128)的事件丢弃
  • BackpressureStrategy.LATEST:只保存最新(最后)事件,超过缓存区大小(128)的事件丢弃(即如果发送了150个事件,缓存区里会保存129个事件(第1-第128 + 第150事件))

另外 RxJava 2.0内部提供 封装了背压策略模式的方法

  • onBackpressureBuffer()
  • onBackpressureDrop()
  • onBackpressureLatest()

默认采用BackpressureStrategy.ERROR模式

        disposable.add(
                Flowable.fromIterable(List<Int>(300) { it })
                        .onBackpressureLatest() // LATEST的背压策略
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe({ Log.i(TAG, it.toString()) }) {
                            Log.e(TAG, it.toString())
                        }
        )
复制代码

RxJava2与Retrofit2

Retrofit支持Observable 作为网络请求方法的返回

interface UserApiInterface {
    @POST("api/passport/login/v3/account")
    fun login(@Body body: RequestBody): Observable<Response<LoginResponse>>
}

inline fun <reified S> createService(serviceClass: Class<S>, baseUrl: String): S {
 val retrofit = Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) // 设定网络请求在子线程
            .addCallAdapterFactory(DeferredCallAdapterFactory.create())
            .client(mHttpClient)
            .build()
        val s: S = retrofit.create(serviceClass)
        return s
    }

复制代码

需求:轮询处理网络请求,错误重试三次的处理

val model = MarketTickerModel(mTickerIdList)
mDisposable.add(
   // 定时5秒轮询
   Observable.interval(0, 5, TimeUnit.SECONDS)
     .flatMap {model.loadData3() }
     .retry(3) // 请求失败重试三次
     .map { it.map { MarketGlobalIndexMapConvertUtils.convertNetWorkData(it) } }
     .subscribeOn(Schedulers.io())
     .observeOn(AndroidSchedulers.mainThread())
     .subscribe({
          showData(it)
      }, { showLoading() })
)
复制代码

需求:同时处理两个网络请求,获取请求结果

    fun loadDataRxJava(composite: CompositeDisposable) {
        composite.add(
            Observable.interval(0, 5, TimeUnit.SECONDS)
                .flatMap {
                    Observable.zip(
                        model.loadData2(),
                        chartModel.loadData2(),
                        BiFunction<List<TickerBase>, List<ChartViewModel>, List<StockViewModel>> { t1, t2 ->
                            t1.map {
                                val tickerBase = it
                                val chartModel = t2.filter { it.tickerId == tickerBase.tickerId }.firstOrNull()
                                val entryList = chartModel?.entryList ?: arrayListOf<Entry>()
                                val maxLength = chartModel?.maxLength ?: 0
                                StockViewModel(
                                    it.tickerId,
                                    it.type,
                                    it.name ?: "--",
                                    CountyResManager.getDrawableResByCounty(it.regionCode),
                                    it.close ?: "",
                                    it.changeRatio ?: "",
                                    entryList,
                                    maxLength
                                )
                            }
                        })
                }
                .retry(3)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe( {
                    getPageView()?.sendUIEvent(ChartUIEvent.ShowTickerData(it, false))
                }, {showError()}))
    }
复制代码
文章分类
Android
文章标签