RxJava- 不是上帝,真不推荐再用了,移动混合开发框架
距离上一次更新关于 RxJava 也有一段时间了,其实这篇文章我早就想写,碍于一直没来得及总结(懒)。所以一直没有成文,今天就来总结
一下我 RxJava 遇到的坑,或者说我为什么不在推荐使用 RxJava。
在我的文章中已经讲过很多次 RxJava 诞生之初就是因为异步。再后来借鉴 LINQ 的思想借用 Monad 的力量使得 Rx 可以使用操作符进行组合将各种复杂的请求简单化。 可以说,RxJava 的设计初衷就是围绕着 Asyhconization 和 Composition。当年的 Netflix 也是为了增加服务器的性能和吞吐量来编写 RxJava 并开源。才使得 RxJava 问世。
再聊聊异步
在那个 RxJava 刚刚火爆的年代,那是一个荒蛮的年代。我们在异步方面资源匮乏,手头仅有 ThreadPool,AsyncTask 和 Handler 这些基础封装的异步库。所以当我们看见 RxJava 这个新奇的小玩意,当我们看到异步还可以这么简单,轻而易举的解决 Concurrency 问题。我们当然如获至宝。 而我们现在选择就更多了,无论是 Java 8 本身提供的CompletableFuture
。还是后起之秀 Kotlin 上的Coroutine
,还有 Android 上官方提供的LiveData
(这里说下: 虽然本质上线程管理仍需用户自己,但是常见的比如 Room 数据库,Retrofit 等等都有现成的 LiveDataAdapter,实际上并不需要我们过多操心线程问题)。 相比之下,RxJava 优势并不那么明显,相反劣势却很突出。
RxJava 门槛太高
相信多数 Android 开发者并没有了解过或者说深入了解过(我自己也没深入了解过)函数式相关的知识。但是如果不了解这些,那么而几乎可以说不可能融会贯通 RxJava 的一些概念。 举个例子,一个很著名的 Googler:Yigit Boyar。也就是每次 IO 的那个大胡子,他的代表作有很多。比如 RecyclerView,再比如 Architecture Component。这样一个 Android 界名人,水平怎么也有平均以上。但是他在实现 LiveData 和 RxJava 适配的时候,同样出现了由于理解上出的问题,造成错误的实现方式。 RxJava 的门槛过于高,就连我自己推广这么久,自己也不敢说对 RxJava 了解有多深刻。经常在常见操作符的使用中出现了或多或少的 unexpected behavior。 再者,无论国内国外的 RxJava 教程水平都参差不齐。新手很难鉴别哪些人说的是对的哪些人说的是错误的。在这样鱼龙混杂的条件下学好这个高门槛的异步库更是变得难上加难。很多教程在自己没有精通的情况下,很容易误导其他人(包括我自己的文章)。
投入高,收获少
虽然这点存疑,因为我自己钻研 RxJava 之后确实觉得收获很大,尤其是经由 RxJava 窥探了函数式的大门。但是功利的看,RxJava 在解决异步处理这个问题上,的确是投入高,收获少。 异步问题是 Android 开发必不可少的一个环节,可以说掌握异步应该是成为入门 Android 开发的敲门砖。而 RxJava 归根到底是通过响应式的方式配合 Monad 来解决异步问题。但是仅仅为了解决异步问题,学习并精通 RxJava 并不是必不可少的。相反,精通 RxJava 需要大量时间和精力,在现在异步编程逐步完善的情况下,完全没有必要。
你永远无法预测你同事的 RxJava 水平
上面几点可能有点抽象,而这点和接下来的几点都是我在实际工作中遇到的实际情况。首先就是你并不能预测或者要求你的同事 RxJava 到达什么样的水平。 我之前的公司使用了一个简单的类 redux 框架。其中 RxJava 是核心部分,他承载了中间 render 层和 view 层的连接。在 Review 同事的代码之后,我才发现 RxJava 还能这么玩?各种奇思妙想的作用让我不得不佩服法国同事的丰富想象力。而这些错误使用就像一颗颗定时炸弹一样埋在代码里。随时可能爆炸。 但是反过来一想,并不是所有人都像我一样喜欢研究 RxJava。他们可能仅仅是因为使用了这个架构而接触 Rx。而 RxJava 的掌握并不是一个 Android 开发的必要条件。他完全可以一点 RxJava 也不会也成为一个优秀的 Android Developer。
RxJava 的行为并不可预期
RxJava 还有一大毛病就是光看方法名你很难知道他的真正意思。 在初学 RxJava 时候,两个一直纠缠不清的问题就是map
和flatMap
的区别。还有flatMap
和concatMap
的区别。 简单的讲map
是一对一,flatMap
是一对 N 的 map 然后在进行flatten
操作。 还有些教程直接写出flatMap
无序,concatMap
有序。 其实这些都只是简单总结,而实际的行为照着相差甚远。 比如flatMap
在第一个 error 的时候会不会继续继续触发第二个?如果我想继续,将如何操作? 再比如concatMap
在遇到第一个Observable
不会中断的时候,怎么继续下一个? 这些都几乎是要看源码或者做多次实验对比才能得出结论的问题,而实际工作中并不想去因为这个工具而去浪费太多时间,得不偿失。但是如果不做,就像前文提到的定时炸弹一样。上线直接增加错误几率。
RxJava 太容易出错
Uncle Ben 说过:
with great power comes great responsibility. RxJava 就是这样。在简单易用的同时他太容易被滥用了。我在实际工作中碰到的例子:
val stationId = "5bCP6Iqx"val statoin:Observable<Station> = staionRepo.getStationById(stationId)val stationLine:Observable<StationLine> = station.flatMap{station ->stationRepo.getLine(station)}
return Observable.merge(station.map{it.toUiModel()},stationLine.map{it.toUiModel()})
乍一看,这几行代码并没有错。这个 Bug 还是后台反馈给我的说为什么 android 每次都会发两个一模一样的请求? 其实问题就出在 stationLine 和 station 并没有共享结果。造成了每次请求都要发两次。 修改后的代码:
val stationId = "5bCP6bif"val statoin:Observable<Station> = staionRepo.getStationById(stationId)
return station.publish{selector ->Observable.merge(selector.map{it.toUiModel()},selector.flatMap{station -> stationRepo.getLine(station)}.map{it.toUiModel()})}
评论