EventBus使用详解

本文的EventBus,是指greenrobot的 EventBus, 主要以EventBus3.0 讲解;

什么是EventBus?

EventBus事件总线, 用于简化Android程序内,各个组件,线程之间的事件传递; 订阅发布模式,将事件的接收者和发布者解耦,一旦publisher发出消息,subscribe自己按需改变; 我个人喜欢把它拿来和BroadCast比较;

在什么场景下使用

  1. 复杂逻辑下的对象传递
  2. 函数的调用者与被调用者需要低耦合,或者框架设计之初,无法预料到的调用

eg. 上面的使用场景,在我们代码中时长出现的场景就是,监听器的传递,回调函数和各种Listener;
比如,在一个activity中,又2个fragment,而每个fragment中又各嵌套一个子fragment, 其中一个子fragment要监听另一个子fragment中的按钮变化; 一般做法是将listener作为函数参数传递, 或者设置为静态变量;
第二个, 就和BoardCast相似

怎么使用

  1. 在gradle中添加依赖
dependencies {
       compile 'org.greenrobot:eventbus:3.0.0'
}
  1. 注册和取消注册
    在要接收消息的类中register unregister, 和广播的注册类似, 一般在activity的 onCreate 和 onDestory 方法中进行
EventBus.getDefault().register( this );
EventBus.getDefault().unregister( this );
  1. 申明处理消息的函数;
    在接收消息的函数上,加上@Subscribe, EventBus是按函数参数的类型确认消息的接收者的, 此函数只能有且仅有一个参数;
@Subscribe(threadMode = ThreadMode.MAIN, priority = 1, sticky = false)
public void onEvent( TestEvent  testEvent ){    
    Log.e( "zy", ">>>> receiverEvent");
}

只需要在函数上加上 @Subscribe 注解即可, 此注解还可以带上额外的参数

  • threadMode , 用于指定此函数运行的线程, 是一个Enum, 有4个常量, MAIN BACKGROUND ASYNC POSTING, 默认为POSTING
    ThreadMode.MAIN 在主线程中运行
    ThreadMode.POSTING 跟消息发送者在同一线程运行
    ThreadMode.BACKGROUND 后台线程, 如果发送消息的线程就是后台线程,就直接执行; 如果不是, 则会把消息放在队列中,依次执行
    ThreadMode.ASYNC 后台线程, 消息会在单独的线程中执行,用了线程池,多个消息会同时执行

  • priority 优先级, 值越小优先级越低,当有多个方法处理同一个消息时,处理的顺序,默认为0

  • sticky 是否接收黏性消息, 和黏性广播相同, 默认为false

  1. 发送消息
    所谓的消息,就只是一个java对象, 发送消息就是把这个对象,传递给处理消息的函数; EventBus消息和EventBus的对象实例有关, 用一个EventBus对象发送的消息,必须是用同一个EventBus对象注册的才能收到消息.
// 发送黏性消息
EventBus.getDefault().postSticky( new TestEvent() );
// 发送普通的消息
EventBus.getDefault().post( new TestEvent() );

发送的消息有2种,
sticky黏性消息, 当消息发送出去之后,如果没有消息接收者处理这个消息,此消息会暂时存储在eventBus实例中, 当后面注册接受者时,如果合适的处理者, 将会把消息给处理者去处理;我个人喜欢用这个来做数据的预加载;

  1. 提升性能, 增加编译时注解处理
    由于android机器本身性能有限,一般不建议使用运行时注解,EventBus的注解声明为Runtime, 但它同时支持编译时注解和运行时注解, 当没配置编译时注解处理器时, 会自动通过反射查找运行时的注解;
    1. 添加注解处理器依赖
buildscript {
    ...
    dependencies {
          classpath 'com.android.tools.build:gradle:2.1.0'
          // 在最外层添加gradle的插件依赖
          classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
      }
  ...
}
// 项目中 增加注解处理器插件
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
       compile 'org.greenrobot:eventbus:3.0.0'
      // 添加注解处理器
      apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
    arguments {
        // 注解处理器 最终生成的java文件位置
        eventBusIndex "com.zy.test.MyEventBusIndex"
    }
}
2. 初始化EventBus时, 使用注解处理器生成的类文件
    ```java  
mEventBus = EventBus.builder().addIndex( new MyEventBusIndex() ).build();
     ```
 EventBus的消息和EventBus实例有关系, 自己配置的EventBus实例,一般需要用单例保存, 确保发送和接收消息的地方,使用的是同一个实例

关于其他的一些细节

- 消息处理者的继承
EventBus的消息处理者,是可以继承的, 父类中的消息处理器, 在子类中仍可使用; 这是一个比较好的功能, 比如通用的消息接收处理,我们在BaseActivity中声明一次, 子类都可以使用了; 此功能可以关闭, 在构建Eventbus实例时, 调用 `EventBus.builder().eventInheritance( false )` ; 官方的说法是关闭后可以提供20%的性能;
  • 黏性消息
    非常实用的功能, 我一般用来做预加载数据; 每种消息类型,最多存储一个黏性消息, 和黏性广播类似; 消息处理者. 声明为sticky = true, 依然可以接收普通消息
  • 进程间的通讯
    Eventbus的发送消息和消息处理是和Eventbus实例有关的, 是无法跨进程传递消息的; 如果涉及到进程间通讯, 还是要使用android系统的接口

对比

  1. Boardcast
    优点: 可以指定运行线程, 消息处理可继承, 代码简单, 消息处理可继承, 低延迟, 对消息数据无要求(不需要实现Parcelable或者Serializable接口)
    缺点: 无法跨进程
  2. LocalBroadcastManager
    这个除了广播的低延迟外, Boardcast的缺点都有, 并且它还不能不能跨进程, 没有黏性广播
  3. RxBus

源码初探

EventBus的源码不多, 这里只讲一下大概, 具体细节大家自己去读源码
源码版本( 66ead83 )

  1. EventBus.java
    此类对外提供所有的接口,register, unregister, post;
    提供一个默认的单例对象, 通过getDefault()获取;
    核心的变量如下
/** eventType和消息接收者存储的map, key是event的class, value是接收者的信息, Subscription中包含的接收消息的对象, 处理消息的方法 */
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
/** 消息处理者和其所包含的能处理的event的Map, key为消息处理者的实例, value为其所能处理的event的类型 */
private final Map<Object, List<Class<?>>> typesBySubscriber;
/** sticky event 的存储的Map, key为event的class, value是具体事件的对象, 每种类型的sticky event 最多存储一个 */
private final Map<Class<?>, Object> stickyEvents;
  1. SubscriberMethodFinder.java
    用于寻找消息处理者的方法, 里面有一个静态变量 Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE 用于保存找到的消息处理这, 加快下一次查找过程

  2. HandlerPoster.java
    HandlerPoster 本质是一个Handler, 使用主线程的Looper, 可以看一下初始化语句 mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); 发送到主线程的消息, 实质都是用此发送一个Message, 然后在Handler#handleMessage中, 调用 Method#invoke(Object, Event);

  3. AsyncPoster.java BackgroundPoster.java
    名称已经很明显, 处理后台消息的2个类; 本质都是Runnable, 都从消息队列中获取消息, 然后在线程池中执行

  4. PendingPostQueue.java
    消息存储的地方, 一个简单的链表结构

  • 注册流程
    register后, 会通过SubscriberMethodFinder#findSubscriberMethods方法, 查找注册的类, 如果添加注解处理器, 会通过反射去查找; 查找后,将各个对应关系保存在Eventbus实例的成员变量; 并且检测是否有黏性消息, 有黏性消息,则立马执行

    post流程, 有ThreadLocal获取所在线程信息, 然后在 Eventbus#subscriptionsByEventType获取所有的消息处理者, 然后判断处理的线程, 分发到各个Poster去处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 前言:EventBus出来已经有一段时间了,github上面也有很多开源项目中使用了EventBus。所以抽空学习...
    Kerry202阅读 1,260评论 1 2
  • 前言:EventBus出来已经有一段时间了,github上面也有很多开源项目中使用了EventBus。所以抽空学习...
    Lauren_Liuling阅读 48,325评论 23 155
  • 目录 1.概述 2.实战 1.基本框架搭建 2.新建一个类FirstEvent 3.在要接收消息的页面注册Even...
    慕涵盛华阅读 10,326评论 2 16
  • 前言 最近在公司做一个类似于手机工厂模式的一个项目,用来检测其他各个App是否正常工作,所以要求是尽可能的轻量级,...
    Luckily_Liu阅读 1,148评论 2 8
  • 概述 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化Android事件传递,这...
    刘涤生阅读 77,712评论 6 57