源碼分析-EventBus的深刻探究

之前,對於activity和fragment之間的通訊可使用接口的方式,定義起來比較麻煩費事,偶然間發現可使用EventBus,發現很好用,查看了一下官方說明:EventBus是針一款對Android的發佈/訂閱事件總線。它可讓咱們很輕鬆的實如今Android各個組件之間傳遞消息,而且代碼的可讀性更好,耦合度更低。可是在用的過程當中總會出現一些問題,下面就將出現的問題詳細記錄一下,順便具體學習EventBus(GreenRobot)這個第三方開源庫,瞭解它內部的實現原理,以致於出了問題能夠快速定位修復。java

連接

官網: greenrobot.org/eventbus/do…git

github: github.com/greenrobot/…github

分析環境

EventBus3.0。
複製代碼

EventBus3.0使用

對於EventBus的原理呢,能夠參照一下官網的這張圖: 緩存

在這裏插入圖片描述

具體的使用方法能夠看官網,很簡單,簡單羅列一下:bash

Add Gradle

compile 'org.greenrobot:eventbus:3.0.0'
複製代碼

Define events

public class MessageEvent {
 
    public final String message;
 
    public MessageEvent(String message) {
        this.message = message;
    }
}
複製代碼

Prepare subscribers

// 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();
}
複製代碼
@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}
複製代碼

Post events

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

以上即是官網給出的簡單使用步驟,也不是很難,因此就不須要翻譯了。接下來咱們針對使用過程當中出現的問題來進行一步一步的深刻探究。併發

踩坑

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

第一個MainActivityasync

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);
    }
}
複製代碼

第二個SecondActivityide

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);
    }
}
複製代碼

第三個ThirdActivityoop

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();
            }
        });
    }
}
複製代碼

打印輸出結果

很顯然,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()中去。

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

一、沒有註冊該事件

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

@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();
}
複製代碼

二、屢次註冊

這是咱們在生命週期中註冊該事件時屢次註冊形成的。解決方法很簡單,能夠判斷一下

//沒有註冊時再進行註冊操做
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後,再次進入,則又會接收到該事件。

清除粘性事件

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;
        }
    }
複製代碼

對於獲取咱們註冊的訂閱方法,首先就是經過緩存來獲取,若是沒有的話則經過如下兩種方式進行獲取:

EventBusAnnotationProcessor註解生成器在編譯期經過讀取@Subscribe()註解並解析,處理其中所包含的信息,而後生成java類來保存全部訂閱者關於訂閱的信息。 運行時使用反射來得到這些訂閱者的信息 對於這兩種方式的分析,能夠參考http://www.jianshu.com/p/f057c460c77e這裏面相關的內容。

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

//**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

該方法流程是:

拿到當前 class 的全部方法 過濾掉不是 public 和是 abstract、static、bridge、synthetic 的方法 過濾出方法參數只有一個的方法 過濾出被Subscribe註解修飾的方法 將 method 方法和 event 事件添加到 findState 中 將 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集合中
  5. 若是是訂閱了粘滯事件的訂閱者,從粘滯事件緩存區獲取以前發送過的粘滯事件,響應這些粘滯事件。

post

  1. 首先獲取當前線程的事件隊列
  2. 將要發送的事件添加到事件隊列中
  3. 根據發送事件類型獲取全部的訂閱者
  4. 根據響應方法的執行模式,在相應線程經過反射執行訂閱者的訂閱方法

unregister

  1. 首先經過unregister方法拿到要取消的訂閱者
  2. 獲得該訂閱者的全部訂閱事件類型
  3. 遍歷事件類型,根據每一個事件類型獲取到全部的訂閱者集合,並從集合中刪除該訂閱者
  4. 將訂閱者從步驟2的集合中移除

關於做者

專一於 Android 開發多年,喜歡寫 blog 記錄總結學習經驗,blog 同步更新於本人的公衆號,歡迎你們關注,一塊兒交流學習~

在這裏插入圖片描述
相關文章
相關標籤/搜索