EventBus:事件总线框架

一、使用场景:

  应用程序的各组件、组件与后台线程间进行通信,比如在子线程中进行请求数据,当数据请求完毕后通过Handler或者是广播通知UI,而两个Fragment之家可以通过Listener进行通信等等。当我们的项目越来越复杂,使用Intent、Handler、Broadcast进行模块间通信、模块与后台线程进行通信时,代码量大,而且高度耦合。此时可使用eventBus进行组件间通信。EventBus能够简化各组件间的通信,让我们的代码书写变得简单,能有效的分离事件发送方和接收方(也就是解耦的意思),能避免复杂和容易出错的依赖性和生命周期问题。

二、eventbus 优势

  1. 简化了组件间交流的方式
  2. 对事件通信双方进行解耦
  3. 可以灵活方便的指定工作线程,通过ThreadMode
  4. 速度快,性能好
  5. 库比较小,不占内存
  6. 使用这个库的app多,有权威性
  7. 功能多,使用方便
  8. 库比较小,不占内存
  9. 使用这个库的app多,有权威性
  10. 功能多,使用方便

三、eventBus原理:

3.1 eventBus组织结构

eventbus的组织结构如下:
在这里插入图片描述
eventbus主要有以下几部分组成:

1、eventbus、asyncEventBus:事件发送器。

2、event:事件承载单元。

3、SubscriberRegistry:订阅者注册器,将订阅者注册到event上,即将有注解Subscribe的方法和event绑定起来。

4、Dispatcher:事件分发器,将事件的订阅者调用
来执行。

5、Subscriber、SynchronizedSubscriber:订阅者,并发订阅还是同步订阅。
  Publisher(发布者)通过post()方法,把Event事件发布出去,Subscriber(订阅者)在onEvent()方法中接收事件。
subscriber ——> EventBus 的register方法,传入的object对象
事件(Event)——> EventBus 的post方法,传入的类型。
publisher(发布者)——> EventBus的post方法。

3.2 运行原理

1、eventbus是基于注册监听的方式来运行的,因此,首先需要将eventbus,然后才会有事件及监听者。新建eventbus或者AsyncEventBus的方式如下:

  • eventBus:
 EventBus eventBus = new EventBus();
  • 或者:AsyncEventBus
 BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(20);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20,
                30, TimeUnit.SECONDS, workQueue);
        AsyncEventBus asyncEventBus = new AsyncEventBus(executor);

2、注册监听者。

eventBus.register(eventListener);

底层就是将类eventListener中所有注解有Subscribe的方法与其Event对放在一个map中(一个event可以对应多个Subscribe的方法)。实现如下:

  void register(Object listener) {
    Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);
 
    for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<Subscriber> eventMethodsInListener = entry.getValue();
 
      CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
 
      if (eventSubscribers == null) {
        CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();
        eventSubscribers =
            MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
      }
 
      eventSubscribers.addAll(eventMethodsInListener);
    }
  }

3、事件发送:执行指定事件类型的订阅者(包含了method),从订阅者中获取指定事件的订阅者,然后按照规则(同步、异步)执行指定的方法。

  • post()
  /** Posts the given event to the event bus. */
    public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }
  • postSticky()粘性事件
 /**
     * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
     * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
     */
    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

四、EventBus 使用:

4.1.创建一个事件类型

  消息事件类型可以是string,int等常见类,也可以是自己自定义一个事件类,方便管理。这边演示,创建了一个EventMessage事件类。

public class EventMessage {

    private int type;
    private String message;

    public EventMessage(int type, String message) {
        this.type = type;
        this.message = message;
    }

    @Override
    public String toString() {

        return "type="+type+"--message= "+message;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

4.2.在需要订阅事件的模块中,注册eventbus。

MainActivity.java

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

    @Override
    protected void onStop() {
        super.onStop();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

4.3、 EventBus的注意事项:

4.3.1注册注意

  注册完了,在不用的时候千万别忘了unregister。
不能重复注册。注册之后,没有unregister,然后又注册了一次。
register与unregister的时间根据实际需求来把控,官方的例子是在onStart()回调方法进行注册,onStop()回调方法进行unregister(),这边根据需求做了改动。
在需要接受事件的类中进行好register之后,需要在该类中创建一个方法来接收事件消息。

@Subscribe(threadMode = ThreadMode.MAIN)
    public void onReceiveMsg(EventMessage message) {
        Log.e(TAG, "onReceiveMsg: " + message.toString());
    }

  创建的这个方法是有要求的。要求有如下几点。
该方法有且只有一个参数。
该方法必须是public修饰符修饰,不能用static关键字修饰,不能是抽象的(abstract)
该方法需要用@Subscribe注解进行修饰。

4.3.2 AsyncEventBus 异部

  在高并发的环境下使用AsyncEventBus时,发送事件可能会出现异常,因为它使用的线程池,当线程池的线程不够用时,会拒绝接收任务,就会执行线程池的拒绝策略,如果需要关注是否提交事件成功,就需要将线程池的拒绝策略设为抛出异常,并且try-catch来捕获异常。如下:

     try {
            eventBus.post(new LoginEvent("iwill", "123456"));
        }catch (Exception exp){
            //TODO 落表或者其他处理
        }

4.4、 @Subscribe 注解介绍

  上面的接收事件的方法中,我们提到,必须要加入@Subscriber注解才可以,这其中的因果我们将在下篇文章进行分析,我们现在所要说的是Subscribe注解的用法。

@Subscribe是EventBus自定义的一种注解,他可接收三个参数。ThreadMode、boolean sticky、int priority。

  所以上面的接收Event方法的代码,完整版的可以这样写:

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
    public void onReceiveMsg(EventMessage message) {
        Log.e(TAG, "onReceiveMsg: " + message.toString());
    }

这三个参数可以根据需要选择是否使用。

4.5 threadMode 参数

  threadMode 参数是用来决定onReceiveMsg将在哪种线程环境下被调用。EvenBus一共有5种Thread mode。

  • POSTING 模式:
    这是EventBus的默认模式,表示post事件是什么线程,onReceiveMsg接收事件方法就在同样的线程环境中执行代码。这种模式,适合使用在执行简单任务的情况下,不需要复杂运算,因为这种模式不需要做线程切换的判断逻辑,直接分发至相同的线程环境,速度快,耗时少。
  • MAIN 模式:
    关于MAIN 这种线程模式,可以和MAIN_ORDERED一起讨论,他们都是表示,无论事件发布在什么线程,事件接收都是在主线程中执行。那MAIN模式和MAIN_ORDERED模式的区别在哪里呢?

  区别在于,对于MAIN 模式,如果事件发布者post事件也是在主线程的话,会阻塞post事件所在的线程。意思就是连续post多个事件,如果接收事件方法执行完,才能post下一个事件。

post(1) ——> onReceiveMsg(1) ———>post(2)———>onReceiveMsg(2)———>post(3)————>onReceiveMsg(3)

如果事件发布者post事件不在主线程的话,连续post多个事件,同是在主线程是接收事件是耗时操作的话,执行的流程会是这样的。是非阻塞的(non-blocking)

post(1)——>post(2)——>psot(3)———>onReceiveMsg(3)

或者

post(1)——>post(2)——>psot(3)———>onReceiveMsg(2)——>onReceiveMsg(3)

  那对于MAIN_ORDERED模式无论事件发布者post在什么线程环境,他的执行流程是都非阻塞的(non-blocking),和MAIN模式 下,post环境不是主线程的执行流程一样。

  • BACKGROUND模式:

  该模式下的时间发布者post线程环境与事件接收onReceiveMsg方法的线程环境关系如下:

post发布环境是主线程的话,事件接收处理的环境是一个子线程。

post发布环境是子线程的话,事件接收处理环境和post发布环境一样。

  • ASYNC 模式:

该模式表示,无论post环境是什么线程,事件接收处理环境都是子线程。

**以上就是EventBus五种线程模式的解读。**上面说了Subscriber注解的ThreadMode参数的含义。接着我们说另外两种参数的含义。

4.6 sticky参数:

  sticky是一个boolean型的参数,默认值是false,表示不启用sticky特性。我们之前说的EventBus事件传递的例子的时候,我们都是先对订阅者(Subscriber)进行先注册的,然后再post事件的。那么sticky特性的作用(sticky==true)是在先post事件,后对订阅者注册这种开发场景的支持的。即先post 再register

4.7 priority参数:

  该参数是int型,默认值是0,比较好理解,就像他的参数名所表示的那样,优先级。值越高,越先接收到事件,不过这里要注意一个问题,那就是优先级的比较前提是在post事件发布,onReceiveMsg事件接收处理这两方的线程环境相同的前提下,才有意义。同是与priority相配合使用的一个方法是cancelEventDelivery.关于它们的使用就不在演示,比较简单。

五、eventBus粘性事件

  因为请求接口然后再发送事件再进行控件的更新。有时候该控件所在的页面可能没有初始化好。这时候eventbus所发送的事件就不会起作用。这时候就要用到粘性事件。粘性事件可以先发送事件,待接收方订阅后接受事件。其实就是解决异步所带来的问题。简单讲,就是在发送事件之后再订阅该事件也能收到该事件,跟黏性广播类似。
实现粘性事件 :
第一步:发送事件的时候不用EventBus.getDefault().post 而是使用 EventBus.getDefault().postSticky;

EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));

第二步:现在粘性事件已发布,开始一个新的活动。 在注册过程中粘性用户所有方法将立即得到前面贴粘性的事件:

首先需要注册订阅事件

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

处理事件

@Subscribe(sticky = true, threadMode = ThreadMode.MAIN) //这种写法达到粘性的目的
public void onEvent(MessageEvent event) {
// UI updates must run on MainThread
textField.setText(event.message);
}

然后在onDestory()方法中取消订阅:防止内存溢出

@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}

这样就达到了目的:发送事件之后再订阅该事件也能收到该事件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值