订阅/发布模式--EventBus使用

简介

       EventBus能够简化各组件间的通信,能有效的分离事件发送方和接收方(解耦),能避免复杂和容易出错的依赖性和生命周期问题。采用的是发布/订阅设计模式。它能简化应用程序内各组件间、组件与后台线程间的通信。在Android常用于Activity、Fragment和后台Service之间通信、传递数据。

pom依赖:


<dependency>
    <groupId>org.greenrobot</groupId>
    <artifactId>eventbus</artifactId>
    <version>3.1.1</version>
</dependency>

实现原理

    每一个eventBus都是订阅对象Subscribe、被订阅对象event的保存者。当订阅对象向eventBus注册自己时,eventBus会解析出Subscribe所订阅的event.并以两个维度记录下来。

               第一个维度subscriptionsByEventType:以Map<event.class, list<Subscription.class>>。其中Subscription是Subscribe和event处理方法的包装。

               第二个维度typesBySubscriber:以Map<Subscribe.class, List<event.class>>记录下来。

    当事件发布者向eventBus发布了even之后,eventBus就会从对应的记录中获取订阅者以及处理方法,再使用反射的方式执行event处理方法

EventBus四要素

  • 事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属的 Class。事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件。
  • 订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 onEvent 函数,这个函数叫事件响应函数。在3.1.1之后可以使用注解的方式来实现响应函数,并不需要固定的函数才能接收订阅数据。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为 0。
  • 发布者(Publisher):发布某事件的对象,发布通过 register 接口将某个订阅添加到事件总线中,unregister 接口退订。通过 post 接口发布事件。
  • ThreadMode :@subscribe注明的函数的执行机制。主要是执行函数所在的线程。

使用流程

  • 第一步 创建一个基本类,也就是事件-消息 用于订阅者和发布者之间传递的的数据。使用 @Subscribe 注释处理方法
@Data
public class Information {
    private String id;
    private String name;

    public Information(String id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override
    public String toString(){
        return ToStringBuilder.reflectionToString(this);
    }
}
  • 第二步 创建一个订阅者,用于订阅发布者发布的消息。
public class SubscribeEvent {
    private Logger LOGGER = LoggerFactory.getLogger(SubscribeEvent.class);
        @Subscribe(threadMode = ThreadMode.XXX)
    public void onAsyncEvent(Information information) {
        if (null == information) return;
        LOGGER.info(String.format("%s【AsyncEvent POSTING模式】接收到事件,消息内容为%s", Thread.currentThread().getName(), information.toString()));
    }
    @Subscribe(threadMode = ThreadMode. XXX) 
    public void onAsyncEvent1(Information1 information1) {
        if (null == information1) return;
        LOGGER.info(String.format("%s【AsyncEvent1 POSTING模式】接收到事件,消息内容为%s", Thread.currentThread().getName(), information1.toString()));
    }
}

四种模式

EventBus3.0有四种线程模型,四种模型都是用来定义执行方式。

先定义一个事件:

/**
 * @author
 * @description 第一步:定义事件,普通的java对象
 **/
@Data
public class AsyncEvent {
    //信息
    private String msg;

    public AsyncEvent(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "msg:" + msg;
    }
}

@Subscribe(threadMode = ThreadMode.POSTING) 

这种模式下,订阅者处理方法的执行和发布者(post()所在线程)使用同一个线程 ,对于一些耗时间的操作会占用大量的时间。所以当方法是耗时操作时,不建议在主线程中试用这种模式。该模式下传递事件开销最小,因为它完全避免了线程切换。--可以使用在java中。

订阅者

public class PostingEventSubscriber {


    /**
     * 默认POSTING模式
     *
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onPostingEvent(AsyncEvent event) {
        if (null == event) return;

        System.out.println(String.format("%s[POSTING模式]接收到事件,%s", Thread.currentThread().getName(), event.toString()));
    }
}

测试方法-发布者:两个不同线程执行发布流程

public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        mainThread.setName("mainThread");

        AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
        EventBus.getDefault().register(asyncEventSubscriber);

        AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
        System.out.println("主线程中发布事件开始");
        EventBus.getDefault().post(asyncEvent);
        System.out.println("主线程中发布事件结束");


        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread childThread = Thread.currentThread();
                childThread.setName("subThread");

                AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
                System.out.println("子线程中发布事件开始");
                EventBus.getDefault().post(asyncEvent);
                System.out.println("子线程中发布事件结束");
            }
        });
        thread.start();
}

执行结果:

主线程中发布事件开始
mainThread[POSTING模式]接收到事件,POSTING:hello world, async
主线程中发布事件结束
子线程中发布事件开始
subThread[POSTING模式]接收到事件,POSTING:hello world, async
子线程中发布事件结束

@Subscribe(threadMode = ThreadMode.MAIN)   

这种模式下,订阅者处理方法方法在主线程中执行,与POSTING 差别不大。在Android开发中使用。

订阅对象:

/**
     * MAIN模式
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMainEvent(AsyncEvent event) {
        if (null == event) return;
        System.out.println(String.format("%s[MAIN模式]接收到事件,消息内容为%s", Thread.currentThread().getName(), event.toString()));
    }

测试-发布者:

public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        mainThread.setName("mainThread");

        AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
        EventBus.getDefault().register(asyncEventSubscriber);

        AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
        System.out.println("主线程中发布事件开始");
        EventBus.getDefault().post(asyncEvent);
        System.out.println("主线程中发布事件结束");

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread childThread = Thread.currentThread();
                childThread.setName("subThread");

                AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
                System.out.println("子线程中发布事件开始");
                EventBus.getDefault().post(asyncEvent);
                System.out.println("子线程中发布事件结束");
            }
        });
        thread.start();
}

结果:


主线程中发布事件开始
mainThread[POSTING模式]接收到事件,POSTING:hello world, async
主线程中发布事件结束
子线程中发布事件开始
subThread[POSTING模式]接收到事件,POSTING:hello world, async
子线程中发布事件结束


@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)

这种模式下,订阅者处理方法和MAIN的方法一样,只不过是判断依据不同--andriod中使用。

/**
     * MAIN_ORDERED模式
     *
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
    public void onAsyncEvent(AsyncEvent event) {
        if (null == event) return;
        System.out.println(String.format("%s[MAIN_ORDERED模式]接收到事件,%s", Thread.currentThread().getName(), event.toString()));
    }

@Subscribe(threadMode = ThreadMode.ASYNC)  

这种模式下,不论发布线程是否为主线程,订阅者处理方法都使用一个空闲线程来执行。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作。

这种模式下,会使用一个线程池来执行线程。线程池是默认的:

ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool()

我们可以自己指定线程池。EventBusBuilder 提供了方法传入我们自己的线程池。

public EventBusBuilder executorService(ExecutorService executorService) {
        this.executorService = executorService;
        return this;
    }

订阅者: 

/**
     * ASYNC模式
     *
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onAsyncEvent(AsyncEvent event) {
        if (null == event) return;
        try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        System.out.println(String.format("%s[ASYNC模式]接收到事件,%s", Thread.currentThread().getName(), event.toString()));
    }

测试-发布者:

public static void main(String[] args) {

        //设置EventBus的线程池
        EventBusBuilder builder = EventBus.builder();
        builder.executorService(Executors.newFixedThreadPool(10));
        EventBus eventBus = builder.build();

        Thread mainThread = Thread.currentThread();
        mainThread.setName("mainThread");

        AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
        eventBus.register(asyncEventSubscriber);

        AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
        System.out.println("主线程中发布事件开始");
        eventBus.post(asyncEvent);
        System.out.println("主线程中发布事件结束");

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread childThread = Thread.currentThread();
                childThread.setName("subThread");

                AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
                System.out.println("子线程中发布事件开始");
                eventBus.post(asyncEvent);
                System.out.println("子线程中发布事件结束");
            }
        });
        thread.start();
    }

 结果:


主线程中发布事件开始
主线程中发布事件结束
子线程中发布事件开始
子线程中发布事件结束
pool-2-thread-1[ASYNC模式]接收到事件,msg:hello world, async
pool-2-thread-2[ASYNC模式]接收到事件,msg:hello world, async


@Subscribe(threadMode = ThreadMode.BACKGROUND)  

这种模式下,在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程(BackgroundPoster)去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作, 以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;使用这种模式的事件处理程序应该尽快返回以避免阻塞后台线程。--可以在java中使用

订阅对象:

/**
     * BACKGROUND模式
     *
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onAsyncEvent(AsyncEvent event) {
        if (null == event) return;
        //暂停线程
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(String.format("%s[BACKGROUND模式]接收到事件,消息内容为%s", Thread.currentThread().getName(), event.toString()));
    }

测试方式-发布者:

public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        mainThread.setName("mainThread");

        AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
        EventBus.getDefault().register(asyncEventSubscriber);

        AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
        System.out.println("主线程中发布事件开始");
        EventBus.getDefault().post(asyncEvent);
        System.out.println("主线程中发布事件结束");

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread childThread = Thread.currentThread();
                childThread.setName("subThread");

                AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
                System.out.println("子线程中发布事件开始");
                EventBus.getDefault().post(asyncEvent);
                System.out.println("子线程中发布事件结束");
            }
        });
        thread.start();

测试结果:


主线程中发布事件开始
主线程中发布事件结束
子线程中发布事件开始
子线程中发布事件结束
pool-1-thread-1[BACKGROUND模式]接收到事件,消息内容为msg:hello world, async
pool-1-thread-1[BACKGROUND模式]接收到事件,消息内容为msg:hello world, async


猜你喜欢

转载自blog.csdn.net/qq_34484062/article/details/109304506
今日推荐