zoukankan      html  css  js  c++  java
  • 事件总线模式

    一、为何使用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
  • 相关阅读:
    让你在PC上调试Web App,UC浏览器发布开发者版
    多态
    深入理解DIP、IoC、DI以及IoC容器
    设计模式之迪米特原则(LOD)(最少知识原则)
    设计模式之合成/聚合利用原则(CARP)
    设计模式之接口隔离原则(ISP)
    设计模式之依赖倒转原则(DIP)
    设计模式之里氏代换原则(LSP)
    OO设计原则
    SQL分页
  • 原文地址:https://www.cnblogs.com/ziyixuedie/p/9071887.html
Copyright © 2011-2022 走看看