EventBus简析

一、概述

EventBus 是Google.Guava提供的消息发布-订阅类库,它实现了观察者设计模式,消息通知负责人通过EventBus去注册/注销观察者,最后由消息通知负责人给观察者发布消息。

二、观察者设计模式

1、目的

定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时候,所有依赖于它的对象都得到通知并被自动更新。

2、应用实例

  1. 接水的时候,接水的人观察水位,当水位到一定程度时候便停止接水;
  2. 老师一布置任务,学生听到都得做。

3、代码

package com.gary.designpattern.observe;

/**

 * 观察者接口IListenner

 * 定义一个update()方法,当被观察者调用notifyListenner()方法时,观察者的update()方法会被回调

 * @author gary

 *

 */

public interface IListenner {

    public void update(String info);

}
package com.gary.designpattern.observe;

/**

 * 被观察者接口ISpeaker

 * 声明添加删除通知观察者方法

 * @author gary

 *

 */

public interface ISpeaker {

    

    public void addListenner(IListenner listenner);

    

    public void removeListenner(IListennerlistenner);

    

    public void notifyListenner();

}
package com.gary.designpattern.observe;

public class Student implements IListenner {

    private String name;

    private String info;

    

    public Student(String name) {

         this.name = name;

    }

    

    @Override

    public void update(String info) {

         this.info = info ;

         read();

    }

    

    public void read() {

         System.out.println("学生:" + name + " 接收到老师的消息 :" + info);

    }

}
package com.gary.designpattern.observe;

import java.util.ArrayList;

import java.util.List;

public class Teacher implements ISpeaker {

    

    private List<IListenner> listennerList;

    private String info;

    

    public Teacher() {

         listennerList = new ArrayList<>();

    }

    @Override

    public void addListenner(IListenner listenner) {

         listennerList.add(listenner);

    }

    @Override

    public void removeListenner(IListennerlistenner) {

         listennerList.remove(listenner);

    }

    

    @Override

    public void notifyListenner() {

         for(int i = 0; i < listennerList.size();i++) {

             IListenner lis = listennerList.get(i);

             lis.update(info);

         }            

    }

    

    public void setInfo(String info) {

         System.out.println("老师发送消息 : " +info);

         this.info = info;

         notifyListenner();

    }

    

}

测试:

package com.gary.designpattern.observe;

public class Test {

    public static void main(String[] args) {

         Teacher teacher = new Teacher();

         teacher.addListenner(new Student("张三"));

         teacher.addListenner(new Student("李四"));

         teacher.addListenner(new Student("王五"));

         teacher.setInfo("大家好");

    }

}

结果:

老师发送消息 : 大家好

学生:张三 接收到老师的消息 :大家好

学生:李四 接收到老师的消息 :大家好

学生:王五 接收到老师的消息 :大家好

三、EventBus使用

1、引入依赖

<dependency>  
        <groupId>com.google.guava</groupId>  
        <artifactId>guava</artifactId>  
        <version>19.0</version>  
</dependency>  

2、示例

public class EventBusCenter {

    private static EventBus eventBus = new EventBus();

    private EventBusCenter() {}

    public static EventBus getInstance() {
        return eventBus;
    }

    public static void register(Object object) {
        eventBus.register(object);
    }

    public static void unregister(Object object) {
        eventBus.unregister(object);
    }

    public static void post(Object object) {
        eventBus.post(object);
    }
}
public class DataListenerTwo {

    @Subscribe
    public void fun(Integer msg) {
        System.out.println("Two fun Integer msg: " + msg);
    }

    @Subscribe
    public void fun2(Integer msg) {
        System.out.println("Two fun2 Integer msg: " + msg);
    }
}
public class DataListenerOne {

    @Subscribe
    public void fun(String msg) {
        System.out.println("One fun String msg: " + msg);
    }

    @Subscribe
    public void fun2(String msg) {
        System.out.println("One fun2 String msg: " + msg);
    }
}

public class TestForEventBus {

    public static void main(String[] args) {
        DataListenerOne one = new DataListenerOne();
        DataListenerTwo two = new DataListenerTwo();

        EventBusCenter.register(one);
        EventBusCenter.register(two);

        EventBusCenter.post("post string");
        EventBusCenter.post(123);

        EventBusCenter.unregister(two);

        EventBusCenter.post("post string");
        EventBusCenter.post(123);
    }

}
One fun2 String msg: post string
One fun String msg: post string
Two fun Integer msg: 123
Two fun2 Integer msg: 123
One fun2 String msg: post string
One fun String msg: post string

3、注意事项

  • 只有通过@Subscribe注解的方法才会被注册金EventBus
  • 方法只能有1个参数
  • 使用基本数据类型的包装类,否则会无法匹配

4、带来的问题

由于EventBus是将消息队列放入到内存中的,listener消费这个消息队列,所以系统重启之后,保存或者堆积在队列中的消息丢失。

解决思路:把内存中的数据持久化到磁盘中。

5、原理图

在这里插入图片描述

6、注意

每次register后都需要进行一次unregister,因为如果不unregister,会让对象无法得到内存回收,导致内存泄漏。所以必须在unregister方法中释放对象所占的内存。

四、源码分析

1、构造方法

默认构造方法:

  /**
   * Creates a new EventBus named "default".
   */
  public EventBus() {
    this("default");
  }

  /**
   * Creates a new EventBus with the given {@code identifier}.
   *
   * @param identifier  a brief name for this bus, for logging purposes.  Should
   *                    be a valid Java identifier.
   */
  public EventBus(String identifier) {
    this(identifier, MoreExecutors.directExecutor(),
        Dispatcher.perThreadDispatchQueue(), 			LoggingHandler.INSTANCE);
  }

  EventBus(String identifier, Executor executor, Dispatcher dispatcher,
      SubscriberExceptionHandler exceptionHandler) {
    this.identifier = checkNotNull(identifier);
    this.executor = checkNotNull(executor);
    this.dispatcher = checkNotNull(dispatcher);
    this.exceptionHandler = checkNotNull(exceptionHandler);
  }

2、订阅者(register)

  public void register(Object object) {
    subscribers.register(object);
  }
  /**
   * 添加订阅者
   * Registers all subscriber methods on the given listener object.
   */
  void register(Object listener) {
    //找到该对象下的所有订阅者
    Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);
	//遍历订阅者map
    for (Map.Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<Subscriber> eventMethodsInListener = entry.getValue();
      //将遍历得到的每一个订阅者添加到对应事件类型的Copy-On-Write容器中
      
      CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);

      if (eventSubscribers == null) {
        CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<Subscriber>();
        //putIfAbsent将新事件类型对应set加到map,如果map中存在则返回原来的value,如果不存在加入新的后则返回null
        //如果返回值为null,那么使用newSet
        eventSubscribers = MoreObjects.firstNonNull(
            subscribers.putIfAbsent(eventType, newSet), newSet);
      }
	  //把该事件所有订阅者加入该事件的set中
      eventSubscribers.addAll(eventMethodsInListener);
    }
  }
  /**
   * 返回map,以事件类型(方法第一个参数类型)为键,订阅者(以eventBus,listener对象,以及method封装而成的订阅者对象)为值
   * Returns all subscribers for the given listener grouped by the type of event they subscribe to.
   */
  private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) {
    Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create();
    Class<?> clazz = listener.getClass();
    //将订阅者加入事件对应的multimap中 (Multimap k唯一 v可以放多个)
    for (Method method : getAnnotatedMethods(clazz)) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      Class<?> eventType = parameterTypes[0];
      methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
    }
    return methodsInListener;
  }
  /**
   * All registered subscribers, indexed by event type.
   * 事件类型 对应的 所有订阅者
   * <p>The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an
   * immutable snapshot of all current subscribers to an event without any locking.
   */
  private final ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>> subscribers =
      Maps.newConcurrentMap();

3、发布者(post)

  /**
   * 将事件发部给所有的订阅者
   * Posts an event to all registered subscribers.  This method will return
   * successfully after the event has been posted to all subscribers, and
   * regardless of any exceptions thrown by subscribers.
   * 如果没有订阅者订阅该事件,那么将会用一个DeadEvent事件包装该事件,重发布
   * <p>If no subscribers have been subscribed for {@code event}'s class, and
   * {@code event} is not already a {@link DeadEvent}, it will be wrapped in a
   * DeadEvent and reposted.
   *
   * @param event  event to post.
   */
  public void post(Object event) {
    //获取事件对应的所有订阅者  
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    //遍历订阅者  
    if (eventSubscribers.hasNext()) {
      //将事件分发给订阅者
      //dispatcher在构造中已经初始化为PerThreadQueuedDispatcher
      dispatcher.dispatch(event, eventSubscribers);
    } else if (!(event instanceof DeadEvent)) {
      // the event had no subscribers and was not itself a DeadEvent
      post(new DeadEvent(this, event));
    }
  }

PerThreadQueuedDispatcher中的dispatch方法

    @Override
    void dispatch(Object event, Iterator<Subscriber> subscribers) {
      checkNotNull(event);
      checkNotNull(subscribers);
      Queue<Event> queueForThread = queue.get();
      queueForThread.offer(new Event(event, subscribers));

      if (!dispatching.get()) {
        dispatching.set(true);
        try {
          Event nextEvent;
          while ((nextEvent = queueForThread.poll()) != null) {
            while (nextEvent.subscribers.hasNext()) {
              //分发事件给订阅者执行
              nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
            }
          }
        } finally {
          dispatching.remove();
          queue.remove();
        }
      }
    }
  /**
   * Dispatches {@code event} to this subscriber using the proper executor.
   */
  final void dispatchEvent(final Object event) {
    executor.execute(new Runnable() {
      @Override
      public void run() {
        try {
          //订阅者执行事件(反射执行方法,即订阅者为方法,事件为参数)  
          invokeSubscriberMethod(event);
        } catch (InvocationTargetException e) {
          bus.handleSubscriberException(e.getCause(), context(event));
        }
      }
    });
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值