美文网首页Android架构设计Android
放弃RxBus,拥抱RxJava(一):为什么避免使用Event

放弃RxBus,拥抱RxJava(一):为什么避免使用Event

作者: W_BinaryTree | 来源:发表于2017-03-31 21:12 被阅读10856次

    EventBus和Otto在之前作为Android组件间通信工具,简单方便十分受欢迎,但是也非常容易Abuse。大概有如下几个缺点:

    • 由于是Event,在发布Event的时候就要做好准备可能并没有人接受这个Event, Subscribe的时候也要做好准备可能永远不会收到Event。Event无论顺序还是时间上都某种程度上不太可控。如果你将数据寄托在Event上然后就直接在Android其他生命周期方法中直接使用这个数据或成员变量。那么很有可能你会得到NPE。
    • EventBus看似将你的程序解耦,但是又有些过了。我们常常使用EventBus传数据,这已经是Dependency级别的数据而不是一个可以被解耦出来的模块。这样就造成了过多EventBus的代码会造成代码结构混乱,难以测试和追踪,违背了解耦的初衷。这时如果有意或无意的造成了Nested Event。那情况会更糟。

    由于EventBus的种种缺点,以及后面RxJava的出现。很多人都开始使用RxJava来取代EventBus。甚至Otto的官方介绍里都写到:

    Deprecated!

    This project is deprecated in favor of RxJava and
    RxAndroid. These projects permit the same event-driven
    programming model as Otto, but they’re more capable and offer better control of threading.

    If you’re looking for guidance on migrating from Otto to Rx, this post
    is a good start.

    链接是一个教你怎么使用RxJava来自己手动写一个RxBus来代替EventBus的文章。虽然看起来是在用RxJava。但是实际上却仍然在用EventBus。甚至这个封装其实也并没有GreenRobot或者Otto来的好。
    我们看看Jake Wharton对RxBus的评价:


    我想"RxBus"唯一的好处就是他是一个Rx的入门毒品。否则的话,你要么就不是在用Rx,要么你需要更加惯用的Rx资源 (渣翻译见谅)

    再来一个GitHub的:


    subscribeActual部分我们先不考虑。然而Jake指出最好不要使用Relay来“重新发明”Event Bus.

    这里看图说话:
    Jake Wharton在GOTO 2016 上的讲座中提到,我们正常的Android编程是这样的:



    我们像一个中间人一样。
    而使用RxJava。 我们的结构,更像这样



    我们使用RxJava来直接把组件相连,对所接受到的数据作出反应,所谓的 "Reactive"。
    而使用Eventbus? Jake 没说, 我自己画一个:

    我们作为一个中间人,传递消息。EventBus作为另一个中间人。帮我们传递消息。(这也就是所谓的“看似解耦”)

    再打个比方,虽然我们将EventBus翻译成时间总线,但是其实总线就是Bus也就是公交车。而RxJava更像一个专车,Uber或者滴滴。他直接链接你的两个或多个需要通信的类。传输数据,当然你可以做一个很大的专车,穿梭在所有类之间,也就是所谓的RxBus。所以在这里为什么放弃RxBus也就不言而喻了不是?

    那么,问题来了?

    怎样才是正确(正常?)的RxJava使用方式?

    其实Jake 也在GitHub的讨论上给出了一个答案:


    所以应该是,每当你想发布一个Event在EventBus时,直接暴露一个Observable出来。每当你想接受一个Event时,找到这个Observable并且Subscribe他。

    这样做的好处是什么?

    • 目标和地点都很明确。你的Subscriber明确的知道他Subscribe的是谁,而且明确的知道我需要作出什么反应。这也正是RxJava的核心“响应式编程”。
    • 由于使用了Observable,对于异常处理将会非常方便。而且还有功能强大全面的Operator来辅助你。
    • 虽然看起来耦合性有所增加。但是这是必要的,上面也说过,EventBus虽然在代码上看似解耦。其实他们还是联系在一起的。而我们这样直接暴露Observable给需要的其他类,这完成了1 -> 1/N的链接,而不需要EventBus这个中间人来传递消息/事件,而且保证我们需要的事件一定会直接到达。

    我们来举个例子

    上下两个Fragment,上面的一个EditText,下面的一个TextView。上面的EditText变化的时候下面的TextView也跟着变化。

    先把EditText的TextChangedListener封装在Observable里:

            stringObservable = Observable.create(e -> editText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
                }
    
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    e.onNext(s.toString());
                }
    
                @Override
                public void afterTextChanged(Editable s) {
    
                }
            }));
    
    /**
    ***
    */
        //Expose Observable
        public Observable<String> getEditTextObservable() {
            return stringObservable;
        }```
    不习惯自己封装可以使用RxBinding :
    
        stringObservable = RxTextView.textChangeEvents(editText)
                                     .map(event -> event.text().toString());
    
    再从我们的TextViewFragment中 取到这个封装好的Observable:
    
    @Override
    public void onStart() {
        super.onStart();
        FragmentEditText fragment = (FragmentEditText) getFragmentManager().findFragmentByTag(FragmentEditText.TAG);
        if(fragment != null){
            fragment.getStringObservable().subscribe(s -> textView.setText(s));
        }
    }
    
    来看看效果:
    
    ![](http:https://img.haomeiwen.com/i2417399/f008642da01d310f.gif?imageMogr2/auto-orient/strip)
    
    当然,这里还有个问题
    - 由于我们将editText封装在Observable里,无论是create()方法还是使用RxBinding,都会持有这个View的强引用。造成内存泄漏。所以我们一定要在最后加入dispose()方法来释放。所以我推荐使用RxBinding,他已经帮我们在dispose()方法里写好了解除Listener的方法。
    - 因为并没有使用publish操作符,导致多个Subscriber的时候还是有些许问题。可以考虑直接加入.share().
    
    具体我们如何讲常用的数据/Callback封装到Observable中。我会在接下来的文章中写到。
    >  [第二篇链接](http://www.jianshu.com/p/d2df6bceeff9) 

    相关文章

      网友评论

      • 7591db869080:大兄弟,多个页面回调刷新?
        W_BinaryTree:很容易的。配合Googole的 ViewModel。 多个Fragment通信十分简单
      • Il_mondo:这无法解决fragment和activity的通信啊
        W_BinaryTree:@wheat7 Architecture Component的 Viewmodel
        wheat7:@W_BinaryTree 同学,是databinding 的 BaseObservable还是架构组件的Viewmodel?
        W_BinaryTree:我目前是使用ViewModel作为中间件来通过Rx通信。无论是从Scope还是通用性上来说都很方便。
      • 30035123f1bc:按照小白我的理解 这就是一个广播和一个回调接口的区别 只是功能可能更多样化了
        W_BinaryTree:广播本身也是回调。Rx也可以做成广播。说白了其实是一个作用域的问题。
      • buhanzhe:终于看懂了 ,豁然开朗。
      • b85958901227:你这个例子,只是1to1,不算 n To n,更别谈 1 to 1/n
        W_BinaryTree:1 to 1/n 可以用share/replay 等操作符完美解决。 n to n 也可以用局部Subject。
      • 577cda791ed6:感谢分享!+1
      • 1014209e010a:可能意思是 rxbus是一辆车给大家一起上。 比较好的方案是 给每个需要的人分配一私家车。爱去哪里就去哪里。
      • 碧海鱼龙:想问下大神,你这个文章是基于rx1.0吗!如何封装一个行为还是有点搞不懂!
        碧海鱼龙:@W_BinaryTree 谢谢大神,我再研究研究!
        W_BinaryTree:基于2.0的。如何封装一个行为可以看下相关的第四篇。不会可以再问我~
      • PaperFish:学习了,感谢大神
      • chiyidun:有点不明白如何替代rxbus,暴露的Observable其他的activity获取到呢?rxbus只要注册一下就行了
        W_BinaryTree:@chiyidun 你是想说在subscribe之后才生产数据吗。 比较简单的可以用subject。但是还是要看具体情况。少量使用subject还是不错的。
        chiyidun:@W_BinaryTree 尝试了一下,请问如果订阅需要在Observable.create之前的话,不知如何处理呢
        W_BinaryTree: @chiyidun 文中只是个简单的例子。 一般配合MVP/ MVVM使用。 再加上share等操作符进行multicast。
      • Persisten:真没看出来和rxbus有什么不同,而且外国大神的rxbus的图 没有注册和释放的描述。
      • 性冷淡_:怎么感觉和rxbus好像啊
      • 谷歌派:大神,下篇啥时候出啊
        W_BinaryTree:已经出了~~
      • d7dba8495ccc:感谢分享!

      本文标题:放弃RxBus,拥抱RxJava(一):为什么避免使用Event

      本文链接:https://www.haomeiwen.com/subject/oknvottx.html