文档章节

动手做一个EventBus(二):实现一个简单的EventBus

大光头兰翔
 大光头兰翔
发布于 2017/05/07 21:32
字数 1167
阅读 578
收藏 31

#EventBus(二)   上一章讲解了Guava的EventBus的使用,这一章会开始模拟EventBus的源码来实现一个简单EventBus。首先要了解事件总线的几个功能模块。demo在github上

##1.Subscriber   Subscriber模块要做的事情就是根据接收到的事件,来执行相应的操作。这里通过Java的反射来实现,通过调用Method的invoke方法来执行一个类中的某个方法。   
###2.1 订阅者 Subscriber.java

//订阅者
public class Subscriber {

	//订阅事件的类
    private final Object target;
	
	//订阅事件的方法
    private final Method method;

    public Subscriber(Object target, Method method) {
        if (target == null || method == null) {
            throw new IllegalArgumentException("Target object and method must not be null");
        }
        this.target = target;
        this.method = method;
        //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查,可以提高性能
        this.method.setAccessible(true);
    }

    public void invoke(Object parameter) throws Exception {
        method.invoke(target, parameter);
    }
}

###2.2 订阅者的注解 Subscribe.java   这里要模拟EventBus中的Subscribe注解,使用注解的好处是被订阅的消息不需要特定地遵守一些约定,只要标注上这个注解,那么就代表订阅这个类型的消息。

/**
 * 标识订阅者
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public [@interface](https://my.oschina.net/u/996807) Subscribe {

}

##2.EventBus   EventBus模块要做的就是

  * 维护一个根据事件找到对应的订阅者的路由

  * 维护一个存储消息的队列

###2.3 事件总线 EventBus.java

//事件总线
public class EventBus {

    //维护key是事件的class,value是这个事件所有的订阅者的映射关系
    private Map<Class<?>, List<Subscriber>> register;
	
	//存放事件的阻塞队列
    private BlockingQueue<Object> queue;

    public EventBus() {
        this.register = new HashMap<>();
        queue = new LinkedBlockingDeque<>();
        new Thread(new Runnable() {
            [@Override](https://my.oschina.net/u/1162528)
            public void run() {
            	//事件的消费者
                Consumer consumer = new Consumer(queue, register);
                consumer.start();
            }
        }).start();
    }

    /**
     * 把类的信息及其subscriber注册到map中去
     */
    public void register(Object listener) {
        if (listener == null) {
            return;
        }
        Class<?> clazz = listener.getClass();
        //找到当前类的所有带有Subscribe注解的方法
        for (Method method : getAnnotatedMethod(clazz)) {
            pushToResisterMap(listener, method);
        }
    }

    private void pushToResisterMap(Object listener, Method method) {
        if (listener == null || method == null) {
            return;
        }
        //获取方法的第一个参数
        Class<?> eventParamter = method.getParameterTypes()[0];
        List<Subscriber> subscriberList;
        if (register.containsKey(eventParamter)) {
            subscriberList = register.get(eventParamter);
            subscriberList.add(new Subscriber(listener, method));
        } else {
            subscriberList = new ArrayList<>();
            subscriberList.add(new Subscriber(listener, method));
            register.put(eventParamter, subscriberList);
        }
    }

    /**
     * 获取类的所有带有Subscribe注解的方法
     */
    private Set<Method> getAnnotatedMethod(Class<?> clazz) {
        Set<Method> annotatedMethods = new HashSet<>();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            Subscribe annotation = method.getAnnotation(Subscribe.class);
            if (annotation != null && !method.isBridge()) {
                annotatedMethods.add(method);
            }
        }
        return annotatedMethods;
    }

    /**
     * 向阻塞队列里发送事件
     */
    public void post(Object event) {
        if (event == null) {
            return;
        }
        try {
            queue.put(event);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

##3.Consumer   消费者要做的就是不断地从队列中获取新的事件,从EventBus获取这个事件的所有订阅者,然后让订阅者执行相应的方法即可    ###2.4 消费者 Consumer.java //消费者 public class Consumer {

    private BlockingQueue<Object> queue;

    private Map<Class<?>, List<Subscriber>> register;

    public Consumer(BlockingQueue<Object> queue, Map<Class<?>, List<Subscriber>> register) {
        this.queue = queue;
        this.register = register;
    }

    public void start() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (true) {
            Object event;
            try {
                //从队列里取事件,如果没有事件就阻塞住
                event = queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
                continue;
            }
            Class<?> clazz = event.getClass();
            if (!register.containsKey(clazz)) {
                System.out.println("Cannot found event's subscriber");
                continue;
            }
            //找到事件的subscriber,然后执行对应的事件
            List<Subscriber> subscriberList = register.get(clazz);
            for (Subscriber subscriber : subscriberList) {
                try {
                    subscriber.invoke(event);
                } catch (Exception e) {
                    System.out.println("Eventbus execute event failed , " + event + "/" + subscriber);
                }
            }
        }
    }
}

###2.5 订阅事件的服务 EventService.java

public class EventService {

    public EventService(EventBus eventBus) {
        eventBus.register(this);
    }

    @Subscribe
    public void handleEvent(HelloEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("handleEvent received: " + event);
    }

    @Subscribe
    public void doHelloEvent(HelloEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("doHelloEvent received: " + event);
    }

    @Subscribe
    public void doGoodByEvent(GoodByEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("doGoodByEvent received: " + event.toString());
    }

    @Subscribe
    public void receiveGoodByEvent(GoodByEvent event) {
        if (event == null) {
            return;
        }
        System.out.println("receiveGoodByEvent received: " + event.toString());
    }
}

class HelloEvent {

    private String greeting;

    private Date date;

    public void setGreeting(String greeting) {
        this.greeting = greeting;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "HelloEvent{" +
                "greeting='" + greeting + '\'' +
                ", date=" + date +
                '}';
    }
}

class GoodByEvent {

    private String saying;

    private Date date;

    public void setSaying(String saying) {
        this.saying = saying;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "GoodByEvent{" +
                "saying='" + saying + '\'' +
                ", date=" + date +
                '}';
    }
}

###2.6 测试一下

public class UnitTest {

    private EventBus eventBus;

    private EventService eventService;

    @Before
    public void init() {
        eventBus = new EventBus();
        eventService = new EventService(eventBus);
    }

    private void postSeveralEvent() {
        HelloEvent helloEvent = new HelloEvent();
        helloEvent.setGreeting("你好啊");
        helloEvent.setDate(new Date());

        GoodByEvent goodByEvent = new GoodByEvent();
        goodByEvent.setSaying("再见啦");
        goodByEvent.setDate(new Date());

        eventBus.post(helloEvent);
        eventBus.post(goodByEvent);
    }

    @Test
    public void run() throws Exception {
        postSeveralEvent();
        while (Thread.activeCount() > 2) {

        }
    }
}

运行以上测试用例可以看到,事件被成功的接收到并执行了

doHelloEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
handleEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
doGoodByEvent received: GoodByEvent{saying='再见啦', date=Sun May 07 21:13:11 CST 2017}
receiveGoodByEvent received: GoodByEvent{saying='再见啦', date=Sun May 07 21:13:11 CST 2017}

  这两篇博客是之前在记录在印象笔记上的笔记,最近有空整理了一下~等有空了还想写一下关于Guice和Jersey的一些博客,之前的公司用了这两个框架。(:зゝ∠)

© 著作权归作者所有

大光头兰翔
粉丝 13
博文 5
码字总数 5403
作品 0
私信 提问
加载中

评论(4)

大光头兰翔
大光头兰翔

引用来自“83年大表”的评论

楼主在哪个公司工作,对事件总线理解的不错。
现在在猫眼电影,这只是Guava的EventBus的一个简单实现啦
83年大表
83年大表
楼主在哪个公司工作,对事件总线理解的不错。
大光头兰翔
大光头兰翔

引用来自“SKYCOBS”的评论

大概是md 不兼容 有一些注解代码出现 链接了

@SKYCOBS 是的我也发现了,应该是osc的问题,我在本机的写md的编辑器没有问题的
SKYCOBS
SKYCOBS
大概是md 不兼容 有一些注解代码出现 链接了
Guava库学习:学习Guava EventBus(一)EventBus

在软件开发过程中,对象信息的分享以及相互直接的协作是必须的,困难在于确保对象之间的沟通是有效完成的,而不是拥有成本高度耦合的组件。当对象对其他组件的责任有太多的细节时,它被认为是...

Realfighter
2014/12/29
0
0
EventBus 3.0使用详解

01 前言 当我们进行项目开发的时候,往往是需要应用程序的各组件、组件与后台线程间进行通信,比如在子线程中进行请求数据,当数据请求完毕后通过Handler或者是广播通知UI,而两个Fragment之...

天王盖地虎626
05/09
0
0
SimpleNews 项目的重构之旅(3) -EventBus 接入

通过需求使用 EventBus 之前就接触过 EventBus ,只是没有在项目中使用过,练习地址 WPEventBusDemo ,今天在项目中接入 EventBus 。 最开始的目的是为了做一个完全退出机制,看了网上很多用...

無名小子的杂货铺
2017/06/02
0
0
Android EventBus 3.1.1最新源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SilenceOO/article/details/89506353 EventBus 3.1.1源码分析 前言 EventBus 是一款在 Android 开发中经常使用...

小黄花的故事
04/25
0
0
Flutter基础-组件通信(父子、兄弟)

上一篇中讲了如何通过父组件给子组件传值: 传送门 这一篇的内容会讲述如何实现: 1. 父子组件之间的传值方法 2. 兄弟组件之间的传值方法 —— eventbus 实现后的效果如下图, 实现效果.png ...

大灰狼的小绵羊哥哥
2018/08/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

带出7个“师弟”,支付宝BASIC College的辅导员是个伪90后

文/电商在线 朱婷 “我的花名是改之,不是‘有则改之无则加勉’的改之,而是‘杨过,字改之’的那个改之。”一见面,他对自己花名的介绍,就让人耳目一新。至于为什么要用杨过的字给自己起名...

阿里云云栖社区
25分钟前
5
0
一次性搞清楚equals和hashCode

前言 在程序设计中,有很多的“公约”,遵守约定去实现你的代码,会让你避开很多坑,这些公约是前人总结出来的设计规范。 Object类是Java中的万类之祖,其中,equals和hashCode是2个非常重要...

编程SHA
25分钟前
1
0
kibana 性能监控 apm

启动本地节点:nohup ./apm-server -c apm-server.yml -e weblogic 12 性能监控 修改 wls12213/user_projects/domains/base_domain/startWebLogic.sh 文件,添加如下内容 export JAVA_OPTION......

internetafei
27分钟前
2
0
给指定的某个commit号加tag并推送

给指定的某个commit号加tag 打tag不必要在head之上,也可在之前的版本上打,这需要你知道某个提交对象的校验和(通过git log获取,取校验和的前几位数字即可)。 git tag -a v1.2 9fceb02 -m...

dragon_tech
27分钟前
1
0
等保2.0来了 | 网站被黑,还要被处罚,为什么背锅的总是我?

“净网2019”专项行动开展以来,四川公安网安部门查处了一批不履行网络安全管理义务的网络运营者,其中包括不履行网络安全保护义务的高校、政府机关、企业和单位均受到了处罚! ▶ 宜宾学院因...

亚洲诚信
29分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部