事件总线模式

一、为何使用EventBus

今天我们介绍一个将耦合度降到极低的框架EventBus

在这之前处理APP全局的监听,大多时候用的是BrodercastReceiver来实现时间的监听。但用起来很不方便, 
BrodercastReceiver在APP中限定只监听系统的广播事件(如:电量,网络等)就OK了。在一些模块或组件之间的时间传递大多用的观察者,在使用的过程中你会发现一个个的回掉,各式各样的参数弄得你头晕目眩。那我们就接触好的框架解决开发中遇见的问题,让自己更轻松的开发。

二、EventBus原理分析

EventBus基于观察者模式的Android事件分发总线。通过事件总线可以实现Activity、Fragment、Service等不同模块或组件之间的通信。避免不同组件和模块之间的耦合问题。方便代码的维护与理解。

事件总线是基于观察者模式的思想实现的,它使用发布订阅的方式支持组件和模块间的通信,摒弃了观察者模式需要显示注册回调的缺点,同时可用于替换Java中传统的事件监听方式。

发布者Publisher:发布某种事件的对象。

事件Event:一个简单的POJO对象。只包含数据,不对数据进行处理。

事件总线EventBus: 负责存储时间信息,处理事件的流动和分发,通过时间总线,事件发布者与订阅者互相不知道对方的存在,是解耦的。事件被发布到总线上({@link #post(Object)})到总线,它将它传递给具有事件类型匹配处理程序方法的订阅服务器。要接收事件,订阅者必须使用{@link #register(Object)}向总线注册自己。注册之后,订阅者将收到事件,直到调用{@link #unregister(Object)}。事件处理方法必须由 {@link Subscribe}必须是public,返回非(空),并且只有一个参数 (事件)。

订阅者Subscriber: 订阅某个事件,一般情况会有一个回掉函数处理接收到的事件。订阅者通过{@link #register(Object)}向总线注册自己,即为订阅事件。订阅者也可以直到调用{@link #unregister(Object)}取消事件订阅。

这里写图片描述

三、EventBus开源库使用(来自greenrobot/EventBus

优点: 
1.简化组件之间的通信 
2.分离事件发送器和接收器 
3.对activity、fragment和线程进行良好的执行 
4.避免复杂和容易出错的依赖关系和生命周期问题。 
5.快速;专为高性能优化 
6.体积小(<50k)

Android studio & Gradle使用分为以下几步:

0.在Gradle中添加在线依赖。

compile 'org.greenrobot:eventbus:3.0.0'
  • 1
  • 2

1.定义消息事件MessageEvent,也就是创建事件类型

public static class MessageEvent { /* Additional fields if needed */ }
  • 1

2.选择你要订阅的订阅者(subscriber)在订阅者里需要用注解关键字 @Subscribe来“告诉”EventBus使用什么方法处理event。

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {/* Do something */};
  • 1
  • 2

注册与注销订阅者。 通常在Android activity与fragment生命周期中注册与注销。

 @Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.发送事件。

EventBus.getDefault().post(new MessageEvent());
  • 1

四、EventBus开源库源码解读

首先,解读@Subscribe,从源码可知,主要有三个参数ThreadMode(线程通信)、Sticky(粘性)、priority(事件优先级)。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

ThreadMode中有四种模式

1.ThreadMode.POSTING 
默认的模式,推荐的模式。 
订阅者将在同一线程中调用,该线程将发布事件。 
此模式完全避免了线程切换,开销最少。

@Subscribe(threadMode = ThreadMode.POSTING)  
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
  • 1
  • 2
  • 3
  • 4

2.ThreadMode.MAIN 
用户将在Android的主线程(有时称为用户界面线程)中调用。 
如果发布线程是主线程,则将直接调用事件处理程序方法。 
使用此模式的事件处理程序必须快速返回,以避免阻塞主线程。

@Subscribe(threadMode = ThreadMode.POSTING)  
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
  • 1
  • 2
  • 3
  • 4

3.ThreadMode.BACKGROUND 
如果post事件的线程不是主线程,则事件处理程序方法将直接在发布线程中调用。 
如果发布线程是主线程,则EventBus使用单个后台线程,该线程将按顺序执行所有事件。 
使用此模式的事件处理程序避免耗时操作,以避免阻塞后台线程。

@Subscribe(threadMode = ThreadMode.BACKGROUND)  
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
  • 1
  • 2
  • 3
  • 4

4.ThreadMode.ASYNC 
事件处理方法在单独的线程中调用。 
发布线程和主线程始终独立。 
发布事件不会等待事件处理程序使用此模式。 
如果事件处理程序的执行可能需要一些时间,例如网络访问,则事件处理程序方法应该使用此模式。 
避免同时触发大量长时间运行的异步处理程序方法,从而限制并发线程的数量。 
EventBus使用线程池有效地重用已完成的异步事件处理程序通知中的线程。

@Subscribe(threadMode = ThreadMode.ASYNC)  
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
  • 1
  • 2
  • 3
  • 4

StickyEvent(粘滞事件)

StickyEvent的特点是,订阅者在发布事件之后订阅该事件,还是可以收到该事件。StickyEvent在内存中保存最新的消息,取消原有消息,执行最新消息,只有在注册后才会执行,如果没有注册,消息会一直保留来内存中。

//发送粘滞事件
EventBus.getDefault().postSticky(new MessageEvent());
  • 1
  • 2
//订阅事件,处理事件
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
  • 1
  • 2
  • 3
  • 4
  • 5
//获取给定类型的最新粘滞事件。
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);

if(stickyEvent != null) {
        //移除并获取给定事件类型的最新粘滞事件。
        EventBus.getDefault().removeStickyEvent(stickyEvent);
        //移除全部的粘滞事件
        // EventBus.getDefault().removeAllStickyEvents();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

removeStickyEvent源代码

 /**
     * Remove and gets the recent sticky event for the given event type.
     *
     * @see #postSticky(Object)
     */
    public <T> T removeStickyEvent(Class<T> eventType) {
        synchronized (stickyEvents) {
            return eventType.cast(stickyEvents.remove(eventType));
        }
    }

    /**
     * Removes the sticky event if it equals to the given event.
     *
     * @return true if the events matched and the sticky event was removed.
     */
    public boolean removeStickyEvent(Object event) {
        synchronized (stickyEvents) {
            Class<?> eventType = event.getClass();
            Object existingEvent = stickyEvents.get(eventType);
            if (event.equals(existingEvent)) {
                stickyEvents.remove(eventType);
                return true;
            } else {
                return false;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

priority事件优先级

用户优先级影响事件传递的顺序。 
在同一传递线程({@link ThreadMode})内,更高优先级的订阅者将在其他优先级较低的其他事件之前接收事件。 
默认优先级为0。 
注意:优先级不影响不同{@link ThreadMode}的订阅者之间的传递顺序!

//priority越大,优先级越高
@Subscribe(priority = 1)  
public void onMessageEvent(MessageEvent event) {
/* Do something */
};
  • 1
  • 2
  • 3
  • 4
  • 5
posted @ 2018-05-22 14:29  紫翼雪蝶  阅读(1551)  评论(1编辑  收藏  举报