android 訊息傳遞機制進階EventBus的深入探究

ostracod發表於2019-03-04

以前,對於activity和fragment之間的通訊可以使用介面的方式,定義起來比較麻煩費事,偶然間發現可以使用EventBus,發現很好用,檢視了一下官方說明:EventBus是針一款對Android的釋出/訂閱事件匯流排。它可以讓我們很輕鬆的實現在Android各個元件之間傳遞訊息,並且程式碼的可讀性更好,耦合度更低。但是在用的過程中總會出現一些問題,下面就將出現的問題詳細記錄一下,順便具體學習EventBus(GreenRobot)這個第三方開源庫,瞭解它內部的實現原理,以至於出了問題可以快速定位修復。

官網: greenrobot.org/eventbus/do…

github: github.com/greenrobot/…

以下使用都是基於EventBus3.0。

EventBus3.0使用

對於EventBus的原理呢,可以參照一下官網的這張圖:

android 訊息傳遞機制進階EventBus的深入探究

具體的使用方法可以看官網,很簡單,簡單羅列一下:

Step 1: Add Gradle

compile 'org.greenrobot:eventbus:3.0.0'複製程式碼

Step 2: Define events

public class MessageEvent {

    public final String message;

    public MessageEvent(String message) {
        this.message = message;
    }
}複製程式碼

Step 3: Prepare subscribers

Subscribers implement event handling methods (also called “subscriber methods”) that will be called when an event is posted. These are defined with the @Subscribe annotation.Note that with EventBus 3 the method name can be chosen freely (no naming conventions like in EventBus 2).

// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}複製程式碼

Subscribers also need to register themselves to and unregister from the bus. Only while subscribers are registered, they will receive events. In Android, in activities and fragments you should usually register according to their life cycle:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}複製程式碼

Step 4: Post events

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));複製程式碼

以上便是官網給出的簡單使用步驟,也不是很難,所以就不需要翻譯了。接下來我們針對使用過程中出現的問題來進行一步一步的深入探究。

踩坑

我們先來使用一個簡單的例子來總結。這個例子主要有三個activity,MainActivity、SecondActivity、ThirdActivity以及一個MessageEvent物件。我們在MainActivity、SecondActivity中分別註冊了MessageEvent事件,在ThirdActivity中post MessageEvent事件,這樣我們在MainActivity、SecondActivity中應該都能接受到該事件。下面是具體的程式碼。

第一個MainActivity

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EventBus.getDefault().register(this);
        Button btn = (Button) findViewById(R.id.button2);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });
    }

    //接收事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> MainActivity");
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        M2Log.d("MainActivity -----------------> onDestroy");
        EventBus.getDefault().unregister(this);
    }
}複製程式碼

第二個SecondActivity

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        EventBus.getDefault().register(this);
        Button btn = (Button) findViewById(R.id.btn2);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(SecondActivity.this, ThirdActivity.class));
            }
        });
    }

    //接收事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> SecondActivity");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        M2Log.d("SecondActivity -----------------> onDestroy");
        EventBus.getDefault().unregister(this);
    }
}複製程式碼

第三個ThirdActivity

public class ThirdActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        Button btn = (Button) findViewById(R.id.btn3);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //傳送訊息
                EventBus.getDefault().post(new MessageEvent(""));
                finish();
            }
        });
    }
}複製程式碼

列印輸出結果

android 訊息傳遞機制進階EventBus的深入探究

很顯然,MainActivity和SecondActivity都接收到了MessageEvent事件。

細心的會發現,我們

EventBus.getDefault().register(this);
EventBus.getDefault().unregister(this);

註冊生命週期是放在onCreate()和onDestroy()中的,如果我們按照官網上來,放在onStart()和onStop()中,你就會發現,我們接收不到MessageEvent事件,可以驗證一下

 @Override
    protected void onStart() {
        super.onStart();
        M2Log.d("SecondActivity -----------------> onStart");
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        M2Log.d("SecondActivity -----------------> onStop");
        EventBus.getDefault().unregister(this);
    }複製程式碼

結果是什麼都不會列印,所以我們一般會將註冊生命週期放到onCreate()和onDestroy()中去。

我們在開發過程中,你會發現有的時候會出現問題:

1、沒有註冊該事件

android 訊息傳遞機制進階EventBus的深入探究

出現這種情況,大多數是沒有註冊該事件,什麼意思呢?就是下面的類似程式碼沒有寫。

 @Subscribe(threadMode = ThreadMode.MAIN)
 public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> MainActivity");
 }複製程式碼

有的人寫了類似的註冊程式碼,但還是會報這個錯誤,那就涉及到註冊的生命週期了。

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}複製程式碼

2、多次註冊

android 訊息傳遞機制進階EventBus的深入探究

這是我們在生命週期中註冊該事件時多次註冊造成的。解決方法很簡單,可以判斷一下

        //沒有註冊時再進行註冊操作
        if (!EventBus.getDefault().isRegistered(this)){
            EventBus.getDefault().register(this);
        }複製程式碼

粘性事件Sticky Events

粘性事件類似於粘性廣播,就是一次註冊永久使用。

如何使用呢?類似於前面的,只不過加了一個sticky = true,傳送時採用postSticky而已

  //釋出事件
  EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));複製程式碼

在接收的時候新增一個sticky = true即可。

  //註冊接收
  @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> SecondActivity");
    }

    @Override
    protected void onStart() {
        super.onStart();
        M2Log.d("SecondActivity -----------------> onStart");
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        M2Log.d("SecondActivity -----------------> onStop");
        EventBus.getDefault().unregister(this);
    }複製程式碼

我們前面出現過一個問題,那就是我們在onStart和onStop中註冊,接收不到EventMessage,通過粘性事件,就可以解決這個問題。不過當你使用粘性事件時你會發現,每次進入註冊該事件的activity中都會主動接收到該事件。

下面是我傳送了一個粘性事件,我們在MainActivity 和 SecondActivity中會接收到該事件,我們退出APP後,再次進入,則又會接收到該事件。

android 訊息傳遞機制進階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
}複製程式碼
/**
 * threadMode 
 * 表示方法在什麼執行緒執行 (Android更新UI只能在主執行緒, 所以如果需要操作UI, 需要設定ThreadMode.MainThread)
 * 
 * sticky     
 * 表示是否是一個粘性事件 (如果你使用postSticky傳送一個事件,那麼需要設定為true才能接受到事件)
 * 
 * priority   
 * 優先順序 (如果有多個物件同時訂閱了相同的事件, 那麼優先順序越高,會優先被呼叫.)
 * */
@Subscribe(threadMode = ThreadMode.MainThread, sticky = true, priority = 100)
public void onEvent(MsgEvent event){
}複製程式碼

上面便是EventBus3.0的常規用法,我們在知道了常規用法後還不行,必須深入瞭解一下它的內部實現原理,否則到時候出了問題後不知道該如何解決,要知其然而之所以然。下面我們便來分析一下它的原始碼。

原始碼解析(EventBus3.0)

原始碼解析部分主要從register、post、以及unregisger這三部分進行分析。

register分析

我們首先從註冊入手,先分析

EventBus.getDefault()複製程式碼

進入原始碼:


static volatile EventBus defaultInstance;

/** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }複製程式碼

EventBus是單例模式存在的,使用了雙重判斷的方式,防止併發的問題,還能極大的提高效率。接著進入register(this)進行分析

//**EventBus.class ---> register**

 /**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        //反射呼叫,獲取訂閱者的類物件
        Class<?> subscriberClass = subscriber.getClass();

        //獲取訂閱者所有的訂閱方法以@Subscribe為註解的一些public方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

        //依次註冊這些訂閱方法
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //對訂閱方法進行註冊
                subscribe(subscriber, subscriberMethod);
            }
        }
    }複製程式碼

其中有獲取訂閱方法的程式碼

List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);複製程式碼

我們進入,分析一下,如何獲取訂閱方法。首先來看一下訂閱方法的類

//**SubscriberMethod.class**

/** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
    final Method method; //方法
    final ThreadMode threadMode; //執行執行緒
    final Class<?> eventType; //接收的事件型別
    final int priority; //優先順序
    final boolean sticky; //粘性事件
    /** Used for efficient comparison */
    String methodString;

    //...省略部分程式碼

    }複製程式碼

SubscriberMethod是一個訂閱方法的實體類,裡面儲存了訂閱方法的一些基本資訊,訂閱方法就是在類中以@Subscribe為註解的一些public方法,注意是public方法否則會報錯,為什麼是public方法我們下面會分析,給出原因,然後進入subscriberMethodFinder.findSubscriberMethods(subscriberClass),該程式碼的作用主要是獲取當前類中所有的訂閱方法。我們來看看是如何獲取一個訂閱者所有的訂閱方法的:

//**SubscriberMethodFinder.class**

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {

        //從快取中獲取訂閱方法
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);

        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        //是否忽略註解器生成的MyEventBusIndex類
        if (ignoreGeneratedIndex) {
            //利用反射來獲取訂閱類中的訂閱方法資訊
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //從註解器生成的MyEventBusIndex類中獲得訂閱類的訂閱方法資訊
            subscriberMethods = findUsingInfo(subscriberClass);
        }

        //當前類中沒有訂閱方法
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            //儲存進快取
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }複製程式碼

對於獲取我們註冊的訂閱方法,首先就是通過快取來獲取,如果沒有的話則通過以下兩種方式進行獲取:

  1. EventBusAnnotationProcessor註解生成器在編譯期通過讀取@Subscribe()註解並解析,處理其中所包含的資訊,然後生成java類來儲存所有訂閱者關於訂閱的資訊。
  2. 執行時使用反射來獲得這些訂閱者的資訊

對於這兩種方式的分析,可以參考www.jianshu.com/p/f057c460c…

對於第一種方法沒什麼好說的,我們來分析一下通過反射來獲取這些訂閱方法的方式,接下來分析通過反射獲取當前類中的訂閱方法

//**SubscriberMethodFinder.class**

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {

        //FindState其實就是一個裡面儲存了訂閱者和訂閱方法資訊的一個實體類,包括訂閱類中所有訂閱的事件型別和所有的訂閱方法等。
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {

            //獲取訂閱方法
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }複製程式碼

程式碼不是太多,裡面涉及到一個類FindState,我們來看下,這是什麼東西,

static class FindState {
        //訂閱方法
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        //以event為key,以method為value
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        //以method的名字生成一個methodKey為key,該method的類(訂閱者)為value
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;
        //...省略部分程式碼
}複製程式碼

這個FindState其實就是一個裡面儲存了訂閱者和訂閱方法資訊的一個實體類,包括訂閱類中所有訂閱的事件型別和所有的訂閱方法等。我們接著分析下面的程式碼。

findUsingReflectionInSingleClass(findState)複製程式碼

這行程式碼便是獲取訂閱方法列表的重要程式碼,我們進入檢視一下:

//**SubscriberMethodFinder.class**

private void findUsingReflectionInSingleClass(FindState findState) {
        //方法
        Method[] methods;
        try {

            //獲取當前類中所有的方法
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }

        //遍歷所有方法
        for (Method method : methods) {

            //獲取方法的訪問修飾權
            int modifiers = method.getModifiers();

            //訂閱方法必須是must be public, non-static, and non-abstract


                //獲取訂閱方法引數型別
                Class<?>[] parameterTypes = method.getParameterTypes();

                //註解方法必須只有一個引數
                if (parameterTypes.length == 1) {

                    //獲取訂閱方法的註解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);

                    //該訂閱方法包含Subscribe註解
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];

                        //新增該註解方法
                        if (findState.checkAdd(method, eventType)) {
                            //該註解方法的執行緒模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();

                            //新增該註解方法
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }複製程式碼

可以看到,首先會得到訂閱類的class物件並通過反射獲取訂閱類中的所有方法資訊,然後通過篩選獲取到訂閱方法集合。這裡面就解釋了為什麼要以@Subscribe為註解的方法,且必須是public型別,方法引數只有一個的原因。


//**Subscribe.java**

@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;
}複製程式碼

註解,分為三種引數,

ThreadMode,方法執行的執行緒,POSTING(預設值)、MAIN、BACKGROUND、ASYNC

sticky,粘性時間,預設值false

priority,優先順序,預設值0

該方法流程是:

  1. 拿到當前 class 的所有方法
  2. 過濾掉不是 public 和是 abstract、static、bridge、synthetic 的方法 過濾出方法引數只有一個的方法
  3. 過濾出被Subscribe註解修飾的方法
  4. 將 method 方法和 event 事件新增到 findState 中
  5. 將 EventBus 關心的 method 方法、event 事件、threadMode、priority、sticky 封裝成SubscriberMethod 物件新增到 findState.subscriberMethods 列表中

通過上面幾步,我們就可以獲得了所訂閱的方法,然後分別進行註冊這些訂閱方法。通過下面的程式碼來執行:

//引數:1訂閱者 2訂閱方法
subscribe(subscriber, subscriberMethod);複製程式碼

接著分析這個註冊方法。

//**EventBus.java**
// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

        //訂閱方法的引數型別,也是事件型別
        Class<?> eventType = subscriberMethod.eventType;

        //訂閱方法描述,實體類(當前類中的訂閱方法)
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

        //獲取當前類中的所有訂閱方法
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

        //該訂閱方法還沒有進行註冊
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            //註冊該訂閱方法
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {

            //已經註冊了,報異常
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        //總的所有的訂閱方法
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {

            //根據優先順序,將訂閱者插入到指定的位置
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //獲取訂閱者所有訂閱的事件型別
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //將該事件型別新增到typesBySubscriber中
        subscribedEvents.add(eventType);

        //如果接收sticky事件,立即分發sticky事件
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }複製程式碼

這裡面涉及到一些物件,我們分別註釋一下:

//Subscription.java

//訂閱者資訊 
final class Subscription {
final Object subscriber;//訂閱者
final SubscriberMethod subscriberMethod;//訂閱方法
}


//subscriptionsByEventType
key訂閱方法型別 values 所有訂閱了該型別的訂閱者集合
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

//typesBySubscriber
key訂閱者 values訂閱事件集合
Map<Object, List<Class<?>>> typesBySubscriber;複製程式碼

瞭解了這幾個物件,上面的程式碼就很容易看懂了,

  1. 首先獲取訂閱方法的引數型別即訂閱事件型別
  2. 根據訂閱事件型別獲取該事件型別的所有訂閱者
  3. 將該訂閱者新增到該事件型別的訂閱者集合中即:subscriptionsByEventType
  4. 獲取訂閱者所有的訂閱事件型別
  5. 將該事件型別新增到該訂閱者的訂閱事件型別集中即:typesBySubscriber

事件post分析

分析了註冊事件後,我們來分析一下分發事件post的流程,首先通過

EventBus.getDefault().post(new MessageEvent(""));複製程式碼

這行程式碼進行事件訊息的分發,我們進入到post中詳細瞭解一下這個流程。

/** Posts the given event to the event bus. */
    public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                //分發事件
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }複製程式碼

程式碼雖然不長,但是不大好理解,這裡面多了一些不常見的物件,我們來看下,首先對於第一行程式碼:

 PostingThreadState postingState = currentPostingThreadState.get();複製程式碼

這裡面的PostingThreadState是什麼意思呢?

/** For ThreadLocal, much faster to set (and get multiple values). */
    final static class PostingThreadState {
        //當前執行緒的事件佇列
        final List<Object> eventQueue = new ArrayList<Object>();
        //是否有事件正在分發
        boolean isPosting;
        //post的執行緒是否是主執行緒
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }複製程式碼

PostingThreadState中包含了當前執行緒的事件佇列,就是當前執行緒所有分發的事件都儲存在eventQueue事件佇列中以及訂閱者訂閱事件等資訊,有了這些資訊我們就可以從事件佇列中取出事件分發給對應的訂閱者。

我們接著分析,對於這個當前執行緒的事件佇列,我們是通過currentPostingThreadState.get();來得到的,對於這個currentPostingThreadState又是什麼呢?

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };複製程式碼

ThreadLocal 是一個執行緒內部的資料儲存類,通過它可以在指定的執行緒中儲存資料,而這段資料是不會與其他執行緒共享的。可以看出currentPostingThreadState的實現是一個包含了PostingThreadState的ThreadLocal物件,這樣可以保證取到的都是自己執行緒對應的資料。

接著就通過postSingleEvent(eventQueue.remove(0), postingState);來對事件進行分發。

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }複製程式碼

事件的分發最後還要通過postSingleEventForEventType它來執行,

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //根據事件型別獲取所有的訂閱者
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                    //分發給訂閱者
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }


//將事件分發給對應的訂閱者
 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }複製程式碼

程式碼比較簡單,它們最終是通過發射呼叫來將事件分發給對應的訂閱者的:

void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }複製程式碼

以上便是事件的分發過程,我們總結概括一下:

  1. 首先獲取當前執行緒的PostingThreadState物件從而獲取到當前執行緒的事件佇列
  2. 通過事件型別獲取到所有訂閱者集合
  3. 通過反射執行訂閱者中的訂閱方法

unregister分析

最後我們來分析一下取消訂閱的方法:

 /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
       //獲取訂閱者的所有訂閱的事件型別
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
            //從事件型別的訂閱者集合中移除訂閱者
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }複製程式碼
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
     //獲取事件型別的所有訂閱者
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //遍歷訂閱者集合,將解除的訂閱者移除
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }複製程式碼

程式碼很簡單,最後總結一下取消訂閱的流程。

總結一下:

  1. 首先獲取訂閱者的所有訂閱事件
  2. 遍歷訂閱事件
  3. 根據訂閱事件獲取所有的訂閱了該事件的訂閱者集合
  4. 將該訂閱者移除
  5. 將步驟1中的集合中的訂閱者移除

以上便是EventBus所有的工作流程,我們來簡單說明一下:

register

1、首先用register()方法註冊一個訂閱者

2、獲取該訂閱者的所有訂閱的方法

3、根據該訂閱者的所有訂閱的事件型別,將訂閱者存入到每個以 事件型別為key 以所有訂閱者為values的map集合中

4、然後將訂閱事件新增到以訂閱者為key 以訂閱者所有訂閱事件為values的map集合中

4.1、如果是訂閱了粘滯事件的訂閱者,從粘滯事件快取區獲取之前傳送過的粘滯事件,響應這些粘滯事件。

post

1、首先獲取當前執行緒的事件佇列

2、將要傳送的事件新增到事件佇列中

3、根據傳送事件型別獲取所有的訂閱者

4、根據響應方法的執行模式,在相應執行緒通過反射執行訂閱者的訂閱方法

unregister

1、首先通過unregister方法拿到要取消的訂閱者

2、得到該訂閱者的所有訂閱事件型別

3、遍歷事件型別,根據每個事件型別獲取到所有的訂閱者集合,並從集合中刪除該訂閱者

4、將訂閱者從步驟2的集合中移除


參考

1、 Android EventBus原始碼解析 帶你深入理解EventBus

2、 EventBus 原始碼解析

3、http://www.cnblogs.com/all88/archive/2016/03/30/5338412.html

4、http://www.jianshu.com/p/f057c460c77e

相關文章