分享

Android 应用内全局通知那种方案更好?观察者、eventbus、本地广播

 quasiceo 2016-01-11

不请自来,见谅。
评价某种方式优劣,有很多种指标,包括空间、时间等性能因素,还有代码的复杂程度,同整个程序的相性等等。

一般 认为本地广播是三种方式中消耗时间、空间最多的一种方式,但也是同 android 相性最好的方式。因为广播属于 android 四大组件之一,在 BroadcastReceiver 中的 onReceive 方法中可以获得 Context、Intent 参数。持有这两个参数便可以调用许多 android sdk 中的方法,这一优势另外两种方式很难弥补的,无论是 EventBus 还是观察者,需要获得 Context 的话,往往都需要进行复杂的参数传递或者是依赖注入。
本地广播另外的一个优点是,许多系统级的事件都是使用广播来进行通知的,像常用的电量变化、网络状态变化、短信发送接收的状态等等。这就使得与 android 系统相关的通知,广播往往成了唯一的选择。

但这并不意味着 android 系统中的通知都应该使用广播,因为相对于其它的方式而言,广播是重量级的、消耗资源较多的方式。广播的优势体现在它与 android sdk 链接的更紧密,当我们需要同 android 交互的时候,广播提供的便捷性抵消掉了它过多的资源消耗。但是对于不需要同 android 交互或是只做很少的交互的时候,使用广播往往是一种浪费

并 且在广播中有一个常见的坑:错误的使用 BroadcastReceiver 的 Context。在 android 的 Application、Activity、Service、ContentProvider、BroadcastReceiver 中都可以获得对应的 Context,但它们并不完全相同。Activity 的 Context 所能做的事是最全的,而其它组件中的 Context 都或多或少的有着功能残缺。就拿常见的弹出 Dialog 来说,不知道有多少新手试图使用非 Activity 的 Context 创建 Dialog 最终无功而返。另外,使用 BroadcastReceiver 等非 Activity 组件的 Context 启动 Activity 也有可能造成隐蔽的错误:当使用非 Activity 组件的 Context 启动 Activity 时,如果不指定 flag 的话,默认会创建一个新的 task,而使用 Activity 的 Context 并且不指定 flag 的话,默认会使用原 task。
Dave smith 的博客中有 一篇文章 详细的介绍了各种 Context 的区别:

EventBus 作为 Android 开发中常用的框架,拥有着许多优点:
  • 调 度灵活。不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递。父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要;通知的优先级,能够保证 Subscriber 关注最重要的通知;粘滞事件(sticky events)能够保证通知不会因 Subscriber 的不在场而忽略。可继承、优先级、粘滞,是 EventBus 比之于广播、观察者等方式最大的优点,它们使得创建结构良好组织紧密的通知系统成为可能。
  • 使 用简单。EventBus 的 Subscriber 注册非常简单,调用 eventBus 对象的 register 方法即可,如果不想创建 eventBus 还可以直接调用静态方法 EventBus.getDefault() 获取默认实例,Subscriber 接收到通知之后的操作放在 onEvent 方法里就行了。成为 Publisher 的过程就更简单了,只需要调用合适的 eventBus(自己创建的或是默认的)的 post 方法即可。
  • 快速且轻量。作为 github 的明星项目之一, EventBus 的源代码中许多技巧来改善性能,关于 EventBus 的性能具体如何,可以点可以点击 这里 查看。

EventBus 的缺点主要集中在它现阶段的实现方式,2.4.0 版是利用反射来实现的(貌似以前的版本也是? )。在 Subscriber 注册的时候,Subscriber 中的方法会被遍历查找以 onEvent 开头的 public 方法。这将带来一些问题,一旦对代码进行混淆,就无法查找到了,所以一个程序既用到了 EventBus 又需要进行代码混淆时,就得设置混淆规则:
-keepclassmembers class ** {
    public void onEvent*(**);
}

# Only required if you use AsyncExecutor
-keepclassmembers class * extends de.greenrobot.event.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}
但这种处理方式,对于保密度要求高的程序而言也是不可接受的,因为这暴露了方法名,给逆向留下了口子。好消息是 EventBus 已经打算使用注解来实现,这应该能够解决代码混淆的问题。

观 察者这种设计模式应当属于程序员的基本功,由于观察者的实现比较简单,因此性能上是三者中最好的,但观察者难以控制通知的优先度,特别是一开始没有考虑优 先度中途更改需求又加入优先度。另外观察者模式要求观察者在事件发生时在场才能收到通知,这就使得观察者有可能遗漏事件,一般来说这并不是问题,但是当程 序要求观察者不能遗漏事件时那就坑了。客观来说,这并不能算作观察者的缺点,因为其它的方式往往也是这样,更加严谨的说法是观察者没有 Eventbus 优先级、粘滞事件的优点。

但有一个缺点是观察者独有的,那就是观察者可能会造成接口的膨胀。特别是当程序要求大量形式各异的通知,而程序员有没有做出良好的抽象时,代码中会包含大量的接口,接口数量的增长又会带来命名、注释等等一大堆问题。本质上说观察者要求程序员从零开始实现事件的产生、分发与处理过程,这就要求参与者必须对整个通知过程有着良好的理解。当程序代码适量时,这是一个合理的要求,然而当程序太大时,这将成为一种负担。

综上来看,广播、EventBus、观察者这三种方式有着它们各自优缺点,具体使用哪一种还是得依靠具体的情况。说了半天感觉什么都没有说,摔...

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多