目录

RxJava doOnNext 歪打正着

来到了快手,代码是真滴乱。RxJava 到处甩,然后又炫技式的使用,冗余各种复杂逻辑,且有些地方使用并不规范,只不过没有出问题。配合自制 MVPs 架构,又秀了一波依赖注入,全是注解,导致我刚看代码时,完全凌乱了,重点是没有任何文档,对新人极不友好,且我认为,代码不应该这样写。

依赖注入可以用,架构可以自己定制,但是逻辑一定要清晰,生命周期或者说状态机一定要越简单越好,越直接越好,而不是外部有状态机,其中一步内部又维护了一套状态,反正就我目前的理解来看,并不好用。

言归正传!RxJava 问题!

为什么很多人推崇 RxJava,然后又有很多人唾弃它

  • 1.推崇派
    如果要去搜那些文章的话,可以balabala说一堆有的没的(现在面试的现状也是凄惨,就是背诵课文的感觉,没啥意思)。
    我个人认为有两点最主要

    • 懒人的幸福
      RxJava 提供了丰富的操作符,你想到的基本他都有,只是一个操作符,可以让你实现 定时、轮询、重试、拦截等等等各种骚操作,按照正常来写,难免有人会写不出,或者状态搞乱,或者线程搞乱。但是有了 RxJava,依葫芦画瓢,你就能实现,且大多情况下没有问题,因为自己理解不到位出现问题的话,基本就是 crash。
    • 拯救强迫症
      无穷无尽的回调地狱让所有人都梦碎,这玩意把回调变成链式的,不知道拯救了多少强迫症,但是其实前端早就有了这类东西,比如我的上一篇 Promise,我觉得比 RxJava 要纯粹和简单
  • 2.唾弃派

    • 学习曲线陡峭 = 不会用 = 瞎几把用 = 谁也说不出个所以然
    • 异常调用栈嵌套一万层 = 不知道错在哪了

我是如何歪打正着的

没来快手之前我也接触过 RxJava,但是基本停留在使用 subscribeOn 和 observeOn 切换线程,他们俩如何影响上下游,最多使用个 flatMap 或者 retryWhen 之类的常用操作符。

doOnNext 的理解

doOnNext这个操作符,在来快手之前,我完全不知道,按照我的理解和看文档,它应当是发生在 onNext 之前,那么其一定和 observeOn 线程关联在一起的,而如果没有指定 observeOn 的情况下呢? 那么 onNext 等方法会执行在当前代码所在线程之中。

所谓当前代码所在线程,Android 中基本就是主线程,但是一般使用 RxJava + Retrofit 进行网络请求的时候,我们都会写上这么两行代码 subscribeOn 和 observeOn,其实就是让网络请求发生在子线程,而在合适的时机切换到主线程更新 UI 或者 其他一些必须在主线程的操作。

这个时候问题就来了,在快手的代码中多是如下操作:

Singleton.get(XXXAPiService.class)
      .sendXXXRequest(xxx)
      .map(new ResponseFunction<>())
      .doOnNext()
      .subscribe()

然后就没了。 起初我并没有在意这个问题,直到代码提交进 dev 分支之后才发现,当天晚上慌的很,因为我没有做线程切换。所以 doOnNext 和 subscribe 要么是在主线程,要么是在子线程,子线程就绝对 crash,我操纵了 UI。主线程也一样,在主线程进行了网络请求。

疑惑

我赶紧掏出测试机,测了一波,打印了 subscribe 所在线程,居然发现没问题,满头问号???
按照之前的理解,是没有问题的,我没有进行网络线程切换,Android 中,则一定是在主线程中间进行了网络请求才对啊? 于是看了看了 Retrofit + RxJava 的代码,主要就是 CallAdapter 的代码。发现在创建 RxJava2CallAdapterFactory 时,其实会传入一个 scheduler 的,这个后面会调用 subscribeOn

更疑惑了

都知道,如果只设置 subscribeOn 而没有设置 observeOn,那么会发生啥?就是后续操作你以为在主线程中的,全在子线程中(前面已经说了原因),那么我的 doOnNext,岂不是···在子线程···,那么必定 crash 才对啊???
感觉打了个 log,发现我的 doOnNext 在主线程···彻底迷了

解惑

因为代码明天要提测了,所以我慌得不行,虽然没有 crash,但是搞不明白原因,一直很不踏实。最后没办法了,只好 debug 一步步看!这才发现了玄机··· 我只看了 retrofit-rxjava 库里的 RxJava2CallAdapterFactory 代码,谁知道快手自己继承了一波 callAdapter.Factory,来了个 CustomAdapterFactory···瞬间崩溃···然后代码都在里面,每次 buildObservable 的时候,都会调用 observeOn 和 subscribeOn 方法来切换线程,不需要你在外部指定。
所以前面的疑惑通通解决了

后怕

所以这次我的 doOnNext 纯粹是歪打正着,也正是因为内部封装的库函数帮忙做了线程切换,才会让我避免出现了线程问题而crash。

但是,我并不感激他们,而且对写这几行代码的人或者做新人入职文档的人表示谴责。

原因:

  • 这种的代码不便于理解
    因为我刚入职,做的第一个需求,稍微急了点,代码基本处于依葫芦画瓢的写法,用之前别人写好的各种类。
    当我发现网络请求不做线程切换的时候其实是很懵逼的,会想到可能内部封装了一层,但是找代码的过程是漫长的,这种东西难道不应该写在新人文档里么?

  • 给人错误的使用习惯
    可以看到,很多人的代码都是在前人的代码上 copy 出来的,他们都不做线程切换,久而久之,是不是有的人就以为不用做线程切换了呢???我觉得肯定会有,人都是惰性思维的,他们可能都没有想这么多。

  • 如果 observeOn 就想在子线程中呢?
    当然这么说有点抬杠了,网络请求中的 observeOn 应该都在子线程中~~
    可是万一有这种奇怪的需求,那岂不是要在外层显示的写上一个 observeOn 却没有 subscribeOn,这让代码更迷惑他人

好的代码是什么样子?

好的代码不是要炫技,不是把 RxJava 的操作符在一个请求中用光,不是说加几个依赖注入,设计一下架构,这就是牛逼了。
做应用开发,迭代如此之快,人员流动也很大,代码的易读性尤其重要,虽然我的代码很烂,但是我都争取让我写的逻辑,我自己可以画出个流程图来,然后配上注释,让别人看得懂。
不是不推崇新技术,我觉得 RxJava 挺好的,依赖注入这种 spring 用烂了的东西也很成熟,但是不要自己想当然开发,要遵守基本原则,不然代码真的一团糟,很难懂啊!!!!