一篇不太一样的 RxJava 介绍,2021 年 Android 开发者跳槽指南
fun getList() : List<MetaData>
fun getBitmap(metaData : MetaData) : Bitmap}
很多同学喜欢 RxJava 都是因为链式调用看起来非常舒服,而链式调用或者说高阶函数或者操作符并不是 RxJava 的专利,Java8 的 Stream API 和 Kotlin 都有相关的操作,比如我们的代码在 kotlin 中可以这样调用
model.getList().map { model.getBitmap(it) }.forEach { showBitMap(it) }
是不是看起来和你们所谓的优雅,简洁的 RxJava 链式调用一样呢?
但是同步意味着阻塞,而网络加载 Bitmap 大家都知道是非常耗时的。为了不阻塞用户界面(UI 线程),我们希望他在后台异步执行,执行后再输出到前台。 所以我们 Android 中最简单直接的方法就是加入 CallBack,来实现异步通信。我们的代码就变成这样
//定义 CallBackinterface CallBack<T> {fun onSuccess(t:T)
fun onError(error:Error)}
interface Model{fun getList(callback:CallBack<List<MetaData>>)
fun getBitmap(metaData:MetaData, callback:Callback<MetaData>)}
看过很多 RxJava 教程的同学肯定觉得这里我要讲 Callback Hell(回调地狱)了,然后开始展示代码 RxJava 来解决回调地狱的问题,但如果这样我这篇文章也没什么意义了,岂不是和很多入门文章都一样了?
我们先来看看为什么我们会出现回调地狱?而在同步的时候却可以保持我们喜欢的**“链式调用”** 我们在同步的时候,我们做的事情可以简化成这样: 进入主界面 -> 通过 getList 方法获取 List -> 根据 list 逐一操作获取 bitmap -> 显示 bitmap 可以看到,我们确实是一条链,所以很简单的通过 stream api 来实现**“链式调用”**。
但是异步的时候呢? 进入主界面 -> getList(callback:CallBack<List>)方法将我们的 CallBack 传给后台 -> 等待后台回调我们的 CallBack
重点来了,与同步的不同,我们这里不是直接获得了我们的 List。而是在等待着异步的另一方通知我们。 同步的时候,我们直接拉取数据 :
而异步的时候,直观的看我们应该是在“等待”数据,异步对象向我们推送数据。
所以在我们的角度,我们是被动的,也就是英语中的 reactive ,也就是所谓的响应式
我们回到我们的例子:
同步的时候,我们是这样的
interface Model{fun getList() : List<MetaData>
fun getBitmap(metaData : MetaData) : Bitmap}
而异步的时候,我们的方法没有了返回值,多了个参数,所以不能使用漂亮的**“链式调用”。 这是因为 List 本身,就是一种同步的类型。我们每次操作 List,都是对 List 来拉取**数据。不信?我们来看下:
大家都知道 List 并不是最基础的集合,常用的集合还有 HashMap,Set,Table,Vector 等等等等。他们都有一个共同的父类: Iterable
interface Iterable<out T> {fun iterator(): Iterator<T>}
这里的 iterator 就是迭代器,他是这个样子的
interface Iterator<out T> {
fun next(): T
fun hasNext(): Boolean}
使用的时候也就是我们最麻烦的迭代方式:
val i = iterator()while(i.hasNext()){val value = i.next()}
所以我们在 Java 中有了 foreach,以及后面的 stream api 等等语法糖。 这里我们看到了,我们每次确实首先询问 List,有没有值,如果有我们获取这个值,如果没有,跳出循环,对 List 的操作结束。读取完毕。
想象一下,如果我们有一种 AsyncList
,对他的读取都是AsyncList
来通知我们,然后再和同步的时候一样使用高阶函数比如 map/foreach 等等该多好。比如
interface Model{fun getList() : AsyncList<MetaData>
fun getBitmap(metaData : MetaData) : Bitmap}
我们就可以像同步一样,
model.getList().map { model.getBitmap(i
t) }.forEach { showBitMap(it) }
现在我们来根据Iterable
设计我们的 AsyncList
,上面我们知道了Iterable
是同步的,是拉取数据,我们需要的AsyncList
是异步的,是他推送数据给我们。 我们和List
一样,给所有的异步集合来一个父类,来设计一个AsyncIterable
,我们知道Iterable
提供Iterator
通过我们主动询问Iterator
的next
,hasNext
等方法我们主动拉取数据。 所以我们的AsyncIterable
理论上来说,应该是我们通过注册AsyncIterator
的方式,将我们的AsyncIterator
传递给AsyncIterable
,让他来通知我们,实现异步和推送数据。 所以我们的AsyncIterable
的实现应该是这样的
interface AsyncIterable<T> {fun iterator(iterator : AsyncIterator<T>)}
(看起来好像有点眼熟?)
我们再来设计AsyncIterator
,同步的方式两个方法,一个是 hasNext,也就是我们主动询问 iterable 接下来之后还有没有值的过程,如果是异步的方式,这应该是我们的AsyncIterable
,来通知我们,他接下来以后还有没有值。 所以变成了这样:
fun hasNext(has : Boolean)
对的,通过这种类似 CallBack 的方式,通知我们有没有值。true 就是还有值,一旦接收到 false,就代表迭代结束,我们的AsyncIterable
已经遍历完成了。 另一个方法 next() 就是我们来主动询问,当前的值是什么。所以我们的AsyncIterable
就是通过这个方法,来通知我们当前的值是什么,依然还是通过类似 CallBack 的方式:
fun onNext(current:T)
(是不是有些眼熟?(手动滑稽))
这里有两个问题: 第一个问题:我们在这里隐藏了一个错误,因为 hasNext()方法返回 false 的时候不一定是没有接下来的值了,也有可能是处理当前值的时候出现了某些个错误或者异常,这样他就不能处理接下来的值,这时候我们的 app 就会崩溃。所以在异步的时候,我们希望我们的AsyncIterable
在出错的时候,可以通知我们他出错了,我们也就不进行接下来的处理了。所以我们有了:
评论