`

我为什么不再推荐 RxJava

阅读更多

本文转自作者: W_BinaryTree 链接:juejin.im/post/5cd04b6e51882540e53fdfa2,如有侵权,可删除

距离上一次更新也有一段时间了,其实这篇文章我早就想写,碍于一直没来得及总结(懒)。所以一直没有成文。来总结一下我RxJava遇到的坑,或者说我为什么不在推荐使用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还能这么玩?各种奇思妙想的作用让我不得不佩服法国同事的丰富想象力。而这些错误使用就像一颗颗定时zd一样埋在代码里。随时可能爆炸。但是反过来一想,并不是所有人都像我一样喜欢研究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不会中断的时候,怎么继续下一个?这些都几乎是要看源码或者做多次实验对比才能得出结论的问题,而实际工作中并不想去因为这个工具而去浪费太多时间,得不偿失。但是如果不做,就像前文提到的定时zd一样。上线直接增加错误几率。

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()})}

RxJava还是过于理想化了

RxJava承诺出一个完美的异步世界,一切异步操作由上游控制,下游只需要思考如何处理,并不关心数据来源。而实际过程中,这个过程还是过于理想化了。最直接的例子就是BackPressure的出现。在数据量足够庞大时,缓存池并不能及时缓存所有生产的数据,造成越积越多最终OOM。也即是所谓的BackPressure。再者,函数式中的Monad来包裹异步这个操作还是过于复杂了,看过RxJava的朋友都应该清楚。某些很简单的操作符在实现起来其实非常复杂。追踪数据十分困难,很容易掉入很难Debug的情况。而且虽然RxJava的文档是我见过少有写的非常出色的库,但是很多操作符如果不读通源码,仅仅从Java Doc和Method Signature来观察,并不清楚期待的行为是什么。就算知道,在一些特殊情况如何处理,仍是一个未知结果。同时RxJava虽然解放了上游控制权力的,也引入了不安全性。如果上游出现了非预想的问题,下游将很难处理。其次,RxJava为了这个理想化的世界,引入了太多的overhead。无论是每个操作符都要生成一个新的Observable实例还是蹦床模式的异步解决方案。都生成了太多的Object在堆中存放。这种overhead在轻量级应用,或者一些小型异步处理比如数据埋点等等行为中,都显得过于庞大。

RxJava起于异步,却也不单单是异步

Rx在被Erik Meijer 提出的时候,确实是由同步的Iterable推导,由主动拉取数据改为被动接受数据。但是在加入函数是Monad的概念之后,RxJava作为响应式数据流,应用在了更多Callback base的场景中。在Android这种GUI平台下尤为出色。多数基于Redux结构的Android架构都或多或少基于RxJava。或者借鉴RxJava的思想。比如Airbnb推出的MvRx。还有Google在18年io中当作Sample App做出的Sunflower,大量使用LiveData。而LiveData无疑也是大量借鉴了RxJava的思想。

总结:RxJava虽然优秀,但并不适合所有人

即使RxJava有且不仅限于我说的上述几个问题,但无疑RxJava仍是一个划时代的优秀的异步框架。但是优秀并不代表适合所有人,我在之前推广RxJava,认为这样的异步基础应该是每一个Android开发者必不可少的知识点。但实际工作使用两年之后,我觉得这并不实际,也不必要。RxJava的水平并不能映射一个Android Dev的开发水平,反之,一个高水平的Android Dev也并不一定对RxJava了解多少。在这样的前提下,再加上入门门槛高,易出错,行为不好预期等等缺点下。在团队没有RxJava Expert的情况下我更倾向于直接弃用RxJava,转为更容易使用的异步框架和响应式数据流。我在入职新公司的的时候,邮件里写了这样一句:

engineering is about trade off

RxJava便是这样一个库,甲之蜜糖,乙之ps。用的好RxJava,他是一个利器,根本离不开。用不好,他就是你身边的定时zd,随时爆炸却又很难拆解。

分享到:
评论

相关推荐

    适配Retrofit RxJava3版本的CallAdapter .zip

    3. **错误处理**:RxJava3对错误处理进行了简化,不再支持onErrorReturn和onErrorResumeNext等方法,而是推荐使用Single和Maybe的onErrorXXX系列方法。CallAdapter需处理网络请求中的异常,并将其转化为合适的错误流...

    Android网络框架(Retrofit+Okhttp+Rxjava)、MVP模式(Dagger)

    大多数应用程序基本都需要连接网络,发送一些数据给服务端,或者从服务端获取一些数据。...可是在 Android 5.0 的时候 Google 就不推荐使用 ...下面我将使Retrofit+Okhttp+Rxjava的方式为大家简述一下网络框架的搭建。

    Node.js-使用JetpackKotlin实现的RxJava自动注销库

    RxJava是Java和Android开发中广泛使用的响应式编程库,它允许开发者处理异步数据流,但如果不正确地管理,可能会导致内存泄漏,因为订阅者可能在不再需要数据时仍然保持活跃。Jetpack库,尤其是Lifecycle组件,是...

    Android rxjava的使用

    在Android中,推荐使用`.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())`这样的链式调用来保证线程安全。 5. **生命周期管理**: 在Android环境中,我们需要注意订阅的生命周期,防止...

    RxJava3-Android-示例:RxJava 3 Android示例-从RxJava 2迁移到RxJava 3-如何在Android中使用RxJava 3

    - **弃用的 API**:部分在 RxJava 2 中的非推荐 API 在 RxJava 3 中已被删除,如 `rx.Completable`,应使用 `io.reactivex.Completable` 替换。 - **线程模型**:RxJava 3 默认使用 `Scheduler.io()` 而非 `...

    Node.js-基于greenrobot的eventBus使用RxJava实现的事件总线

    5. **RxJava结合**: 为了利用RxJava的功能,我们需要将EventBus的发布和订阅转换为RxJava的数据流。这可以通过创建一个辅助方法完成,如下所示: ```java public static Observable&lt;Object&gt; toObservable(EventBus...

    RxFcm:适用于Android Firebase Cloud Messaging的RxJava扩展(aka FCM)

    :warning: 不推荐使用:此图书馆不再受管。 :warning: 尺寸 用于Firebase Cloud Messaging的RxJava扩展,它是一种体系结构方法,可以在处理推送通知时轻松满足android应用程序的要求。 特征: 删除android样板代码...

    rxjava-2.2.21.jar中文-英文对照文档.zip

    (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以...

    Rxjava2_Flowable_Sqlite_Android数据库访问实例

    2. **订阅与取消订阅**:通过RxJava的订阅机制,开发者可以随时订阅或取消订阅,不再依赖于复杂的回调函数,使得代码更清晰,更易于维护。 3. **数据流处理**:RxJava提供了一种流式处理数据的方式,允许我们对查询...

    rx-realm,一个围绕领域java的轻量级包装器,它向sql操作引入了反应流语义(灵感来自square/sqlbrite).zip

    然而,值得注意的是,Rx-Realm 已经不再被推荐使用。自 Realm Java 0.87.0 版本起, Realm 自身已经内置了对反应式编程的支持,因此开发者可以直接利用 Realm 的新特性而无需额外的封装库。这表明 Realm 团队认识到...

    Handler 推荐用法demo

    - **避免内存泄漏**:确保在不再需要Handler时调用`removeCallbacksAndMessages(null)`,以防止Handler持有对Activity的引用导致内存泄漏。 - **使用静态内部类**:为Handler创建一个静态内部类,避免因匿名内部类...

    之前一些好用的架包Android架包自动初始化id,以及GsonFormat等等

    3. **其他可能的Android库**:压缩包中可能还包括其他常用的Android库,比如Retrofit用于网络请求,Glide或Picasso进行图片加载, Dagger或Hilt实现依赖注入,RxJava和RxAndroid提供异步编程支持,还有EventBus或...

    ActivityGroupDemo

    总的来说,虽然ActivityGroup在现代Android开发中已经不再推荐使用,但它代表了Android早期的一种解决方案,理解其工作原理有助于我们更好地掌握Android应用开发的历史和演变。现在,我们应该更多地使用Fragment和...

    Android将bitemap在SD卡中的保存与读取

    同时,随着Android版本更新,`recycle()`可能不再推荐使用,更倾向于依赖系统的内存管理。 通过以上步骤,Android开发者能够高效地处理Bitmap的保存与读取,从而优化应用的性能和用户体验。理解并掌握这些技巧,...

    Android-HTTP.rar_android

    2. **HttpClient**:虽然Android从API 23开始已不再推荐使用Apache HttpClient,但在较低版本中,它仍然是一个常用的HTTP库。HttpClient提供了更简洁的API,方便开发者进行网络请求,但可能带来额外的内存占用和维护...

    Android 自定义加载框 网络访问

    - **RxJava/RxAndroid**:如果使用了RxJava,可以订阅网络请求并使用`subscribeOn(Schedulers.io())`和`observeOn(AndroidSchedulers.mainThread())`来控制在哪个线程执行操作,同时在适当的时候发送信号显示或隐藏...

    安卓Android源码——listview实现图片的异步加载.zip

    通过这份“安卓Android源码——listview实现图片的异步加载.zip”资料,开发者可以了解到如何在实际项目中实现高效的图片异步加载,提升应用性能,同时掌握相关优化技巧,为用户提供更流畅的体验。实践这些知识,将...

    android的post请求

    需要注意的是,从Android 6.0开始,系统不再默认包含对HttpClient的支持,因此在新项目中推荐使用OkHttp,它具有更好的性能和更简单的API。同时,对于网络请求,务必考虑异步执行,以免阻塞主线程,可以使用...

    Android 图片缓存、加载器

    Google推荐的缓存库如Picasso、Glide或Fresco等,都实现了这样的双层缓存策略。这些库通常会自动管理缓存,包括图片的加载、解码、缩放以及缓存到内存和磁盘的过程。例如,Glide通过其内部的BitmapPool来优化内存...

    android bitmap layout

    - 使用` recycle() `方法回收不再使用的Bitmap,但要注意不能在回收后再次使用。 5. **Bitmap的优化** - 使用InMemoryCache或LruCache缓存Bitmap,减少重复解码。 - 使用Picasso、Glide或 Fresco 等库,它们内置...

Global site tag (gtag.js) - Google Analytics