天天看点

EventBus的配置,黏性事件以及订阅者优先级Sticky EventsPriorities and Event Cancellation

        EventBus使用解决组件间通讯的一个开源项目,我们平时在使用GreenDao的时候,首页上也会看到它的身影,可以点击查看,下面就来看看如何使用这个简单实用的开源项目。因为考虑到月底了,这个月才写了一篇博客,所以,我可能根据EventBus的文档介绍分为几篇来写(虽然每一部分都比较简单,哈哈)

参考文档           上一篇:EventBus事件处理线程模式    下一篇:订阅者索引,混淆以及异步执行器

        本篇主要介绍EventBus的配置,黏性事件事件以及订阅者的优先级,下面就来逐一介绍。

1,EventBus的配置

 我们之前在代码中获取EventBus实例的时候都是都过下面代码实现:

EventBus.getDefault()
           

其实我们查看源码可以发现,EventBus的实例都是通过类构建器实现的。下面就来看看上面的一句代码在源码中“经历”了哪些步骤

第一步:getDefault()代码

public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
           

上面这段可以看出来,通过这一方法获取的实例都是同一个对象defaultInstance,下面就来看看defaultInstance怎么获取的。

第二步:defaultInstance的获取

defaultInstance = new EventBus();


 /**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }
           

其实,它是通过EventBus的一个构造方法获取,并且在这个构造方法内部实现的时候,又调用了this(DEFAULT_BUILDER),下面就需要搞明白两个东西,一个是this(DEFAULT_BUILDER)的代码,另一个是DEFAULT_BUILDER是什么“东西”。

第三步:DEFAULT_BUILDER和defaultInstance的获取实现

//DEFAULT_BUILDER是一个类构建器
 private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

//this(DEFAULT_BUILDER)的源码
EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }
           

到这里我们就明白了原来那一句代码经历这么多,不管多少我们明白了一个事情,那就是EventBus的实例都是通过类构建器实现的,说到builder,那自然是因为便于配置EventBus的对象属性了。如果不忙,也可以看看我的另一篇关于类构建器的博客。

下面就来看看那些属性是可设置的:

    boolean logSubscriberExceptions = true; //订阅者异常日志是否开启

    boolean logNoSubscriberMessages = true;//没有订阅者消息异常是否开启

    boolean sendSubscriberExceptionEvent = true;//是否发送订阅者事件异常

    boolean sendNoSubscriberEvent = true;//如果没有订阅者是否发送事件

    boolean throwSubscriberException;//是否抛订阅者异常

    boolean eventInheritance = true;//有继承关系的事件的订阅者是否接收该事件

    boolean ignoreGeneratedIndex;//是否忽略索引

    boolean strictMethodVerification;//是否开启方法严格验证

    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;//在ThreadMode.ASYNC模式下的线程池

    List<Class<?>> skipMethodVerificationForClasses;//不进行方法验证的类

    List<SubscriberInfoIndex> subscriberInfoIndexes;//订阅者索引信息

    Logger logger;//日志打印者

这个时候只需要查看构建器(EventBusBuilder)的源码就可以了:

package org.greenrobot.eventbus;

import android.os.Looper;

import org.greenrobot.eventbus.meta.SubscriberInfoIndex;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Creates EventBus instances with custom parameters and also allows to install a custom default EventBus instance.
 * Create a new builder using {@link EventBus#builder()}.
 */
public class EventBusBuilder {
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

    boolean logSubscriberExceptions = true;
    boolean logNoSubscriberMessages = true;
    boolean sendSubscriberExceptionEvent = true;
    boolean sendNoSubscriberEvent = true;
    boolean throwSubscriberException;
    boolean eventInheritance = true;
    boolean ignoreGeneratedIndex;
    boolean strictMethodVerification;
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
    List<Class<?>> skipMethodVerificationForClasses;
    List<SubscriberInfoIndex> subscriberInfoIndexes;
    Logger logger;
    MainThreadSupport mainThreadSupport;

    EventBusBuilder() {
    }

    /** Default: true */
    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
        this.logSubscriberExceptions = logSubscriberExceptions;
        return this;
    }

    /** Default: true */
    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
        this.logNoSubscriberMessages = logNoSubscriberMessages;
        return this;
    }

    /** Default: true */
    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
        return this;
    }

    /** Default: true */
    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
        this.sendNoSubscriberEvent = sendNoSubscriberEvent;
        return this;
    }

    /**
     * Fails if an subscriber throws an exception (default: false).
     * <p/>
     * Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, you won't miss
     * exceptions during development.
     */
    public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
        this.throwSubscriberException = throwSubscriberException;
        return this;
    }

    /**
     * By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified).
     * Switching this feature off will improve posting of events. For simple event classes extending Object directly,
     * we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be
     * >20%.
     * <p/>
     * However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app,
     * unless it is posting at high rates, e.g. hundreds/thousands of events per second.
     */
    public EventBusBuilder eventInheritance(boolean eventInheritance) {
        this.eventInheritance = eventInheritance;
        return this;
    }


    /**
     * Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced
     * setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior.
     */
    public EventBusBuilder executorService(ExecutorService executorService) {
        this.executorService = executorService;
        return this;
    }

    /**
     * Method name verification is done for methods starting with onEvent to avoid typos; using this method you can
     * exclude subscriber classes from this check. Also disables checks for method modifiers (public, not static nor
     * abstract).
     */
    public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
        if (skipMethodVerificationForClasses == null) {
            skipMethodVerificationForClasses = new ArrayList<>();
        }
        skipMethodVerificationForClasses.add(clazz);
        return this;
    }

    /** Forces the use of reflection even if there's a generated index (default: false). */
    public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
        this.ignoreGeneratedIndex = ignoreGeneratedIndex;
        return this;
    }

    /** Enables strict method verification (default: false). */
    public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
        this.strictMethodVerification = strictMethodVerification;
        return this;
    }

    /** Adds an index generated by EventBus' annotation preprocessor. */
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if (subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

    /**
     * Set a specific log handler for all EventBus logging.
     * <p/>
     * By default all logging is via {@link android.util.Log} but if you want to use EventBus
     * outside the Android environment then you will need to provide another log target.
     */
    public EventBusBuilder logger(Logger logger) {
        this.logger = logger;
        return this;
    }

    Logger getLogger() {
        if (logger != null) {
            return logger;
        } else {
            // also check main looper to see if we have "good" Android classes (not Stubs etc.)
            return Logger.AndroidLogger.isAndroidLogAvailable() && getAndroidMainLooperOrNull() != null
                    ? new Logger.AndroidLogger("EventBus") :
                    new Logger.SystemOutLogger();
        }
    }


    MainThreadSupport getMainThreadSupport() {
        if (mainThreadSupport != null) {
            return mainThreadSupport;
        } else if (Logger.AndroidLogger.isAndroidLogAvailable()) {
            Object looperOrNull = getAndroidMainLooperOrNull();
            return looperOrNull == null ? null :
                    new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
        } else {
            return null;
        }
    }

    Object getAndroidMainLooperOrNull() {
        try {
            return Looper.getMainLooper();
        } catch (RuntimeException e) {
            // Not really a functional Android (e.g. "Stub!" maven dependencies)
            return null;
        }
    }

    /**
     * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be
     * done only once before the first usage of the default EventBus.
     *
     * @throws EventBusException if there's already a default EventBus instance in place
     */
    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

    /** Builds an EventBus based on the current configuration. */
    public EventBus build() {
        return new EventBus(this);
    }

}
           

2,黏性事件

Sticky Events

Some events carry information that is of interest after the event is posted. For example, an event signals that some initialization is complete. Or if you have some sensor or location data and you want to hold on the most recent values. Instead of implementing your own caching, you can use sticky events. So EventBus keeps the last sticky event of a certain type in memory. Then the sticky event can be delivered to subscribers or queried explicitly. Thus, you don’t need any special logic to consider already available data.

黏性事件

一些事件携带的信息对于那些发布了之后才订阅的订阅者也很有用,比如,一个标识初始化完成的事件。再比如你想保留最近的传感器或者位置信息,使用了黏性事件你就不用自己缓存了。因为EventBus会在内存中缓存某些类型的黏性事件,这些事件可以被发送或者查询;所以你不用为这些保存在内存中的数据做任何特殊的(缓存)逻辑处理。

简而言之一句话,通过postSticky发布的黏性事件,订阅者可以在发布之后订阅该事件并收到之前发布的黏性事件,而且这些黏性事件还可以被查询到。

简单使用:

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

// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {   
    textField.setText(event.message);
}
           

我们也可以手动删除EventBus中的黏性事件:

MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
    // "Consume" the sticky event
    EventBus.getDefault().removeStickyEvent(stickyEvent);
    // Now do something with it
}
           

3,订阅者优先级与事件取消

Priorities and Event Cancellation

While most use cases of EventBus do not need priorities nor event cancellation, they may come in handy in some special cases. For example, an event may trigger some UI logic if the app is in the foreground, but react differently if the app is currently not visible to the user.

很少使用,一般在一些特殊的场合会用到,比如某些事件触发UI逻辑当应用处在前台的时候,但是当其进入进入后台之后,应用就需要作出不同的响应。

使用方法:

@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
    ...
}

@Subscribe
public void onEvent(MessageEvent event){
    // Process the event
    ...
    // Prevent delivery to other subscribers
    EventBus.getDefault().cancelEventDelivery(event) ;
}
           

由上可见,优先级主要通过订阅者的priority设定,其值越大,优先级越高,默认是0;注意这里的高低要相对同一种线程模式哟。 取消事件是通过EventBus的cancelEventDelivery()方法实现。

4,简单演示

(1)我们先来演示一个EventBus配置的小例子,在上面我们说到了一个参数eventInheritance,这个参数表示发布子类事件,父类能不能收到,默认是true,那我们就来验证一下。

  首先创建三个类,MSGEventFather,MSGEventMe,MSGEventSon,三个的继承关系是Son继承Me,Me继承Father,下面看一下具体的代码。

package com.hfut.operationeventbus;

/**
 * author:why
 * created on: 2019/2/22 16:01
 * description: event for test
 */
public class MSGEventFather {
    String msgInfo;

    public MSGEventFather() {
        msgInfo = "father:default info";
    }

    public MSGEventFather(String msgInfo) {
        this.msgInfo = msgInfo;
    }

    public void setMsgInfo(String msgInfo) {
        this.msgInfo = msgInfo;
    }

    public String getMsgInfo() {
        return msgInfo;
    }
}



package com.hfut.operationeventbus;

/**
 * author:why
 * created on: 2019/2/22 19:40
 * description: event for test
 */
public class MSGEventMe extends MSGEventFather {
    private String msgInfo;

    public MSGEventMe() {
        super();
        msgInfo = "me:default info";
    }

    public MSGEventMe(String eventInfo) {
        super();
        this.msgInfo = eventInfo;
    }

    public String getMsgInfo() {
        return msgInfo;
    }

    public void setMsgInfo(String msgInfo) {
        this.msgInfo = msgInfo;
    }

}



package com.hfut.operationeventbus;

/**
 * author:why
 * created on: 2019/2/22 19:40
 * description: event for test
 */
public class MSGEventMe extends MSGEventFather {
    private String msgInfo;

    public MSGEventMe() {
        super();
        msgInfo = "me:default info";
    }

    public MSGEventMe(String eventInfo) {
        super();
        this.msgInfo = eventInfo;
    }

    public String getMsgInfo() {
        return msgInfo;
    }

    public void setMsgInfo(String msgInfo) {
        this.msgInfo = msgInfo;
    }

}
           

处理业务逻辑类如下(两个):

package com.hfut.operationeventbus;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

/**
 * @author why
 * @date 2019-2-22 15:54:08
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "MainActivitytest";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.send_MsgEvent).setOnClickListener(this);
        EventBus.getDefault().register(this);//订阅消息
    }

    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onMsgEventFather(MSGEventFather event) {
        Log.e(TAG, "我是爸爸:" + "发布的是:" + event.getMsgInfo());
    }

    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onMsgEventMe(MSGEventMe event) {
        Log.e(TAG, "我是我:" + "发布的是:" + event.getMsgInfo());
    }

    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onMsgEventSon(MSGEventSon event) {
        Log.e(TAG, "我是儿子:" + "发布的是:" + event.getMsgInfo());
    }
    
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.send_MsgEvent:
                startActivity(new Intent(MainActivity.this, PublisherActivity.class));
                break;
        }
    }
}



package com.hfut.operationeventbus;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import org.greenrobot.eventbus.EventBus;

import java.util.Timer;
import java.util.TimerTask;

/**
 * @author why
 * @date 2019-2-27 16:37:11
 */
public class PublisherActivity extends AppCompatActivity {

    int index=0;
    private static final String TAG = "PublisherActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_publisher);
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if(index==0) {
                    EventBus.getDefault().postSticky(new MSGEventFather("爸爸"));
                }
                else if(index==1){
                    EventBus.getDefault().postSticky(new MSGEventMe("我"));
                }
                else {
                    EventBus.getDefault().postSticky(new MSGEventSon("儿子"));
                    timer.cancel();
                }
                index++;
            }
        }, 1000,2000);
    }
    
}
           

日志结果如下:

EventBus的配置,黏性事件以及订阅者优先级Sticky EventsPriorities and Event Cancellation

日志结果验证这个参数的含义,你也可以通过构建器设置这个参数为false试一下。注意要保证订阅和发布使用的同一个EventBus对象。如下:

package com.hfut.operationeventbus;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

/**
 * @author why
 * @date 2019-2-22 15:54:08
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "MainActivitytest";
    public static EventBus bus=EventBus.builder().eventInheritance(false).build();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.send_MsgEvent).setOnClickListener(this);
        bus.register(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onMsgEventFather(MSGEventFather event) {
        Log.e(TAG, "我是爸爸:" + "发布的是:" + event.getMsgInfo());
    }

    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onMsgEventMe(MSGEventMe event) {
        Log.e(TAG, "我是我:" + "发布的是:" + event.getMsgInfo());
    }

    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onMsgEventSon(MSGEventSon event) {
        Log.e(TAG, "我是儿子:" + "发布的是:" + event.getMsgInfo());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.send_MsgEvent:
                startActivity(new Intent(MainActivity.this, PublisherActivity.class));
                break;
        }
    }
}


package com.hfut.operationeventbus;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import org.greenrobot.eventbus.EventBus;

import java.util.Timer;
import java.util.TimerTask;

/**
 * @author why
 * @date 2019-2-27 16:37:11
 */
public class PublisherActivity extends AppCompatActivity {

    int index=0;
    private static final String TAG = "PublisherActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_publisher);
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if(index==0) {
                    MainActivity.bus.postSticky(new MSGEventFather("爸爸"));
                }
                else if(index==1){
                    MainActivity.bus.postSticky(new MSGEventMe("我"));
                }
                else {
                    MainActivity.bus.postSticky(new MSGEventSon("儿子"));
                    timer.cancel();
                }
                index++;
            }
        }, 1000,2000);
    }

}


           

日志结果如下:

EventBus的配置,黏性事件以及订阅者优先级Sticky EventsPriorities and Event Cancellation

其他的分析我这里就不验证了,因为确实较为简单。

注:欢迎扫码关注

EventBus的配置,黏性事件以及订阅者优先级Sticky EventsPriorities and Event Cancellation