转自:http://blog.csdn.net/huangyanan1989/article/details/10858695

场景描述

当一个Android应用功能越来越多的时候,保证应用的各个部分之间高效的通信将变得越来越困难。

在应用中的多个地方,控件经常需要根据某个状态来更新他们显示的内容。这种场景常见的解决方式就是定义一个接口,需要关注该事件的控件来实现这个接口。然后事件触发的地方来注册/取消注册这些对该事件感兴趣的控件。

例如,陌陌依赖手机位置信息来获取附近的用户,所以在位置更新管理器(MmLocationManager)中定义了一个接口来监听位置更新的事件(MmLocationListener):

帮助
1
2
3
interface MmLocationListener {
  void onLocationChanged(Location location);
}

然后在应用的各个需要响应该事件的地方来实现上面的接口,然后在位置更新管理器(MmLocationManager)中注册/取消注册事件监听接口的实现类:

帮助
1
mLocationManager.get().register( this );

当不在需要监听的时候,取消注册

帮助
1
mLocationManager.get().unregister( this );

问题

上面的解决方案是没问题的,但是不是理想方案。每个控件实现这个接口,导致这些控件和位置管理器注册强耦合在一起。这还意味着,当单元测试的时候,您需要模拟(mocked)位置管理器来生成位置更新事件。

随着应用功能的增加,需要监听的事件越来越多,而越来越多的控件需要监听不同的事件,则导致越来越多的控件需要注册到各种事件管理器上:

帮助
1
2
3
4
5
6
// 代码开始变得无法控制…
mLocationManager.get().register( this );
userAuthenticator.get().register( this );
settingsManager.get().register( this );
syncManager.get().register( this );
configurationMonitor.get().register( this );

注意:上面的每个事件的注册,都要实现对应的时间更新接口。

注册和取消注册这些事件慢慢的会变得越来越难以管理。导致测试越来越困难,并将导致开发者的效率越来越低,同时在您的应用中越来越容易引入各种奇怪的Bug。

解决方案

为了找出该问题的优雅解决方案,从一个意想不到的的地方借鉴点经验 — Swing应用。 Event Bus模式 — 也被称为Message Bus或者发布者/订阅者(publisher/subscriber)模式 — 可以让两个组件相互通信,但是他们之间并不相互知晓。

和需要注册各个事件的监听器相比,一个组件现在只用在Event Bus上注册一次即可:

帮助
1
bus.register( this );

上面的注册告诉Event Bus我们现在希望接收各个事件的更新。 然后Bus检测该类中每个带有@Subscribe注解的函数,当相关的事件发生的时候就调用这些带有注解的函数。

上面示例中的位置监听功能,不用实现位置监听接口和里面的函数了,只需要提供一个带有@Subscribe注解的函数即可:

帮助
1
2
3
4
@Subscribe
public void locationChanged(LocationChangedEvent event) {
    // TODO React to location change.
}

现在Event Bus会把所有的LocationChangedEvent 事件都发送给上面的函数。

现在 MmLocationManager 类不用注册监听器了,当位置改变的时候 只需要向Event Bus发布事件即可:

帮助
1
bus.post( new LocationChangedEvent( 37.892818 , - 121.772608 ));

这样 组件间相互解耦了,而单元测试也变得简单了。任何事件都可以发布给Event Bus,然后Event Bus会找到对该事件感兴趣的函数来调用。

注意:您也许已经发现该模式在Android上层也存在 — Intent系统就是这样设计的!

下面介绍两个Android系统的Event Bus模式类库。

Otto — Android系统的Event Bus类库

Otto是Square公司在他们应用中使用的Event Bus实现。从Guava中演变而来,并且专注于Android平台。

通过使用Otto,Square公司的应用组件间不紧密耦合了,单元测试也更加容易了。

您可以通过Otto项目的主页来了解更多内容或者查看Otto项目的源代码。

EventBus — Android系统的Event Bus类库

EventBus 是http://greenrobot.de 出品的另外一个Event Bus类库,功能稍微多一点。

EventBus使用起来和Otto差不多,分订阅、注册、发布、取消注册等步骤:
在订阅者类中实现各种事件的订阅函数
public void onEvent(AnyEventType event) {}
把该订阅类注册到Bus中
eventBus.register(this);
向Bus发布事件
eventBus.post(event);
不需要的时候 取消订阅事件
eventBus.unregister(this);

otto介绍

Otto 是Android系统的一个Event Bus模式类库。用来简化应用组件间的通信。关于Event Bus模式的详细情况,请参考这里。

Otto的使用是比较简单的,先到项目主页下载源码:https://github.com/square/otto

下载后的源码目录中包含一个library和sample目录, library目录是类库源代码;sample目录是示例代码。

主要使用com.squareup.otto.Bus类、@Produce、 @Subscribe 注解。

在组件的相关生命周期中通过Bus类的register 函数来注册,然后Bus类会扫描改类中带有@Produce和 @Subscribe 注解的函数。

@Subscribe 注解告诉Bus该函数订阅了一个事件,该事件的类型为该函数的参数类型;而@Produce注解告诉Bus该函数是一个事件产生者,产生的事件类型为该函数的返回值。

可以在Activity或者Fragment的onResume函数中注册监听器;在onPause函数中取消注册:

  @Override protected void onResume() {super.onResume();// Register outselves so that we can provide the initial value.BusProvider.getInstance().register(this);}@Override protected void onPause() {super.onPause();// Always unregister when an object no longer should be on the bus.BusProvider.getInstance().unregister(this);}

对于在前面文章中介绍的场景,可以定义一个位置产生函数:

  @Produce public LocationChangedEvent produceLocationEvent() {// Provide an initial value for location based on the last known position.return new LocationChangedEvent(lastLatitude, lastLongitude);}

该函数的@Produce注解告诉Bus该函数可以产生一个类型为LocationChangedEvent的事件,当有该类型的事件向Bus注册的时候, Bus会调用该函数获取初始值,并使用该值来调用订阅该事件的函数。

而对于需要订阅该事件的地方,通过@Subscribe注解来告诉Bus:

  @Subscribe public void onLocationChanged(LocationChangedEvent event) {locationEvents.add(0, event.toString());if (adapter != null) {adapter.notifyDataSetChanged();}}

需要注意的是,不管是生产者还是订阅者都需要向Bus注册自己:

bus.register(this);

如果您认为在每个Activity或者Fragment的onResume和onPause函数中都需要调用bus.register(this)和bus.unregister(this)函数比较麻烦的话,可以通过一个Bus包装类来自动
完成注册的工作,然后在您的类中只需要继承基类,并调用函数getScopedBus().register(…) 来注册需要的对象即可。详细情况参考示例代码:https://gist.github.com/3057437

执行线程

Otto的事件调用默认是在主线程(应用的UI线程)中调用,

// 下面两种声明方式是一样的效果.
Bus bus1 = new Bus();
Bus bus2 = new Bus(ThreadEnforcer.MAIN);

如果您不关注在那个线程中执行事件函数,则可以通过 ThreadEnforcer.ANY 参数来初始化Bus对象,

如果上面2个线程控制方式满足不了您的需求,您还可以通过实现ThreadEnforcer接口来定义自己的线程模型、

或者使用EventBus类库,我们将在下一篇文章中介绍EventBus和Otto的区别。

EventBus 使用介绍

和Otto相比, EventBus主要有3点不同:

1. 事件订阅函数不是基于注解(Annotation)的,而是基于命名约定的,在 Android 4.0之前 的版本中,注解解析起来比较慢 , 事件响应函数默认以“onEvent”开始,可以在EventBus中修改这个值,但是不推荐这么干
2. 事件响应有更多的线程选择
EventBus可以向不同的线程中发布事件,在ThreadMode 枚举中定义了4个线程,只需要在事件响应函数名称“onEvent”后面添加对应的线程类型名称,则还事件响应函数就会在对应的线程中执行,比如事件函数“onEventAsync”就会在另外一个异步线程中执行,ThreadMode定义的4个线程类型如下:
PostThread :事件响应函数和事件发布在同一线程中执行。这个是默认值,这样可以避免线程切换。
MainThread :事件响应函数会在Android应用的主线程(大部分情况下都是UI线程)中执行。
BackgroundThread :事件响应函数会在一个后台线程中执行。如果事件发布函数不是在主线程中,则会立即在事件发布线程中执行响应函数。如果事件发布函数在主线程中,EventBus则会在唯一的一个后台线程中按照顺序来执行所有的后台事件响应函数。

上面的3种事件响应函数,应该能够很快的执行完,不然的话会阻塞各自的事件发布。

async:事件响应函数在另外一个异步线程中执行。该线程和发布线程、主线程相互独立。如果事件响应函数需要较长的时间来执行,则应该使用该模式,例如 网络访问等。需要注意的是,由于系统并行的限制,应该避免在同一时间触发大量的异步线程。 EventBus使用一个线程池来提高线程的效率。

3. EventBus支持 Sticky Event

有时候某个事件可能会用到多次,比如在前面介绍Event Bus模型一文的示例中,最新的位置更新信息,可能需要多次用到,真对这种情况,您可以把该事件发布为Sticky Event,然后,当需要查询该信息的时候,可以通过Bus的getStickyEvent(ClasseventType) 函数来查询最新发布的Event对象。
同一类型的事件只保存最新的Event对象。
注册和发布事件的函数分别为 registerSticky(…) 和 postSticky(Object event)
在项目主页上还有和Otto之间性能对比的表格,以及性能测试的源码。

EventBus介绍相关推荐

  1. EventBus介绍与使用

    ps:虽然EventBus是Android中的一款开源框架,但是它确值得任何一个开发者学习 如果你学习过设计模式,那么你一直知道观察者模式.,既然能想到这个设计模式,那么就来看一个Android开源框 ...

  2. EventBus使用介绍

    EventBus介绍 EventBus是一种用于Android的事件发布-订阅总线,由GreenRobot开发,它利用发布/订阅者者模式来对项目进行解耦,它简化了应用程序内各个组件之间进行通信的复杂度 ...

  3. Android消息传递之EventBus 3.0使用详解

    前言: 前面两篇不仅学习了子线程与UI主线程之间的通信方式,也学习了如何实现组件之间通信,基于前面的知识我们今天来分析一下EventBus是如何管理事件总线的,EventBus到底是不是最佳方案?学习 ...

  4. EventBus使用总结和使用场景

    一.EventBus介绍 EventBus是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间.组件与后台线程间的通信.传统的事件传递方式包括:Handle ...

  5. Android开源框架——事件总线 EventBus3.0讲解

    开源项目地址 官网:https://github.com/greenrobot/EventBus 介绍 EventBus主要使用了观察者模式设计方案.主要替代Intent.Handler. Broad ...

  6. Android基础 EventBus3 0实用教程

    作为一个Android开发者,我们在日常的开发中肯定会使用到EventBus,比如说当我们在做app的消息模块的时候,接收到后台推送的消息之后,为了方便用户查看,就需要把消息保存到本地,正常情况下在页 ...

  7. 双非同学,自学编程,毕业一年逆袭百度!

    大家好,我是鱼皮,最近秋招快到了,我就想着给大家找一些优秀的程序员面经分享.希望可以帮助准备求职的同学更稳地上岸,同时帮助学编程的小伙伴们更好地明确学习路线和方法. 今天要分享的呢,是一位 双非非科班 ...

  8. eventbus使用_Android EventBus框架的使用介绍

    EventBus 是Android和Java数据通信的一个简捷框架,在Android中,主要用于线程直接的通信以及Activity,Fragment等之间的数据交互等 官方介绍 简化组件之间的通信,解 ...

  9. EventBus设计与实现分析——特性介绍

    EventBus是一个 发布/订阅 模式的消息总线库,它简化了应用程序内各组件间.组件与后台线程间的通信,解耦了事件的发送者和接收者,避免了复杂的.易于出错的依赖及生命周期问题,可以使我们的代码更加简 ...

最新文章

  1. [hdu5225][BC#40]Tom and permutation
  2. 用python画玫瑰花教程-使用Python画一朵玫瑰花
  3. python网课一般多少钱-排名前十的python零基础编程在线网课一对一费用多少钱
  4. eclipse插件egit安装使用
  5. [转]宝文!Apple Push Notification Service (APNS)原理与实现方案
  6. python 网页调试_使用Django 2.0构建Python Restful Web服务:七)在浏览器中浏览和调试restful接口...
  7. UITextView自定义placeholder功能:用一个label写了文字,然后当检测到长度不为0的时候就把label隐藏...
  8. 平稳时间序列分析:ARMA模型
  9. 基于netty框架的JTT808/JTT905/JTT1078协议客户端
  10. PYTHON判断回文素数
  11. ZZM区块链全球区块文化娱乐相结合的新型网站源码
  12. 高瓴投的澳斯康生物冲刺科创板:年营收4.5亿 丢掉与康希诺合作
  13. 【web前端】H5图片制作
  14. 「冰河技术」部分精华文章分类汇总,P8架构师都在看的技术文章!!
  15. 网络游戏运营策略分析
  16. 四则运算web版需求规格说明书
  17. 停车管理系统程序c语言,停车场管理系统C语言实现
  18. 关于NFV需要了解的知识
  19. 燕园思达刘有林董事长一行 参观考察延安高新技术产业开发区
  20. CAD入门图形和标注1

热门文章

  1. mysql数据字段属性
  2. 智力题及答案(包含梅氏砝码问题)
  3. JSP页面和html页面中文乱码的解决
  4. Vue UI组件库(Element UI库)
  5. 解决electron-vue打包错误问题,nsis和winCodeSign下载失败问题
  6. javascript实现数字补全不足补零
  7. 转载之-中值滤波均值滤波
  8. 计算机一级wps选择题必背知识点,计算机一级《WPS》提高练习题及答案
  9. digitalpersona 开发
  10. 凸优化—凸松弛(Convex Relaxation)