个人对EventBus和回调的一些思考

EventBus是近年来在Android开发中非常常用的一个通信框架,利用它可以很轻易地实现组件与组件之间的各种通信,无论跨线程与否。

而回调则是在Android开发中哪儿都能见到的编程逻辑,常见的onCreate()等生命周期方法以及OnXXXListener这些都是回调来的。

在EventBus出来之前(或许还有RxAndroid之类的,不过我还没使用过Rx系列,所以暂且不提),在Android开发中,组件间的通信或者前后端线程之间的通信都是通过持有引用的回调或者Handler来实现的。

使用回调有什么不好呢?

  1. 强引用,可能导致内存泄漏。
  2. 多层回调的代码逻辑可读性差,调试时甚至使人抓狂。
  3. 直接用匿名内部类去实现很容易就出现多级缩进,长长的屏幕都看不完一行代码。
  4. 要跨线程必须要用Handler。

而EventBus是可以替代回调的。但替代的时候并不是那么简单地在原来调用回调方法的地方调用EventBus的post方法即可。如果只是这样简单的替换,还是有可能产生你意料之外的状况的。

使用EventBus替代回调的时候,应当有以下步骤:

  1. 把回调里传的数据抽离成一个消息类Msg,一个回调方法对应一个消息类。
  2. 在调用回调方法的地方把调用改为EventBus的post方法,参数是上一步中抽取出来的Msg类。
  3. 把原来实现了的回调方法改为用Subscribe注解标明该在哪类线程运行,并把原参数改为抽取出来的Msg类,在具体实现里再从Msg实例取出对应的数据。
  4. 在添加回调的代码处改为EventBus的register方法;并在所处的组件销毁时的回调方法(如onDestroy、onDestroyView等,注意要和register方法所处的生命周期对称)内调用EventBus的unregister方法。

看样子似乎就这样做是没问题的。

但是,考虑一个情况,Fragment A 和 Fragment B都需要同一类消息Msg,然后从Fragment A里调用了耗时操作去请求Msg,在还没获得之前就退出了A进入到B,然后B又去请求,然后一直待在B那里的话,那么B就会收到两次Msg,一次是A请求的,一次是自己请求的,那么这时候B对消息Msg的处理逻辑就有可能出错,因为一般来说这种通信式的逻辑是非幂等的。

针对这种情形有两种解决方案:

  1. 持有耗时操作的引用,在调用EventBus的unregister方法时,同时调用耗时操作的取消方法。
  2. 在Msg内部再加一个id的属性来区别这个Msg的来源。

我建议是使用第一种解决方案,因为这个解决方案涉及的代码改动量是更少的。如果采用第二种解决方案的话,那你得在所有涉及到Msg这个类的构建修改的操作逻辑那里添加对id属性透传的逻辑,修改的代码越多越容易引入新问题

那加上这一步之后是不是就高枕无忧了呢?

不一定,EventBus是不支持post null的,那么之前在回调中,有使用到null来进行判断的逻辑就得改了,这是其一。第二点就是List、Map这类泛型类型是很不合适作为Msg类的,原因就是Java的类型擦除会把泛型对应的具体类型信息擦除掉,而EventBus却恰恰就是利用这个信息来进行消息分发的,两个都Subscribe了的方法,如果它们的接收参数都是List,即使一个是List,一个是List,EventBus在分发List类型的消息时是两个方法都会被调用的,这样如果不在方法内部再进行一次类型判断,很容易就会造成崩溃。

基于我最近的使用经历来看,如果真的按以上的做法来写好了,那确实EventBus是很好地替代了回调的功能。

那么EventBus有没有缺点呢?哈哈哈哈当然有啦。

  1. 只要用得一多,那消息类的数量必然是会爆炸性增长。
  2. 调试的时候除非熟悉整块逻辑,不然不跑起来你是没办法了解Subscribe的方法的数据来源。

说完两个的缺点,再说说优点。

回调的:

  1. 绝对的一一对应,非常精确。
  2. 使用回调可以使得逻辑的设计思路很清晰。

EventBus的:

  1. 松耦合。
  2. 使用简单。

以上就是我对EventBus和回调的使用思考。还是颇为浅显,日后若有更深的理解了再开一文。

你可能感兴趣的:(个人对EventBus和回调的一些思考)