天天看點

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