框架源碼解析之EventBus源碼分析

一、簡述

EventBus 是安卓中的一個事件總線庫,可用於替代廣播,Handler和用於組件化中組件間通信的庫。

EventBus

這是EventBus的Github上的一張介紹圖,從圖中可以理解EventBus的工作流程,發佈者即 Publisher 發佈事件到EventBus中,通過EventBus將事件傳遞給觀察者即Suncriber。

二、EventBus的使用
  1. 添加依賴

​ 在 module的gradle中添加

implementation 'org.greenrobot:eventbus:3.1.1'
  1. 定義事件類型

定義一個實體類,作爲傳遞的事件。

public class EventMessageType {

    private int type;

    private String data;

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}
3. 事件註冊

在Actiivty的onCreate中調用 EventBus.getDefault().register(this),將該Activity的實例註冊到EventBus中,然後需要爲接收事件的方法添加 @Subscribe註解。在該註解類型中可以設置接收事件所在的線程,優先級,以及是否是粘性事件。當頁面銷燬的時候需要調用 EventBus.getDefault().unregister(this) 解綁註冊。

public class EventBusAct extends AppCompatActivity implements OnClickListener {

    private TextView tvDesc;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_event);
        tvDesc = findViewById(R.id.tv_desc);
        tvDesc.setOnClickListener(this);
        EventBus.getDefault().register(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(String msg) {
        tvDesc.setText(msg);
    }

    @Override
    protected void onDestroy() {
        EventBus.getDefault().unregister(this);
        super.onDestroy();
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        switch (v.getId()) {
            case R.id.tv_desc:
                intent.setClass(this, EventBusSecAct.class);
                break;
        }
        startActivity(intent);
    }
}
  1. 發佈事件

發佈事件通過EventBus發佈一個事件,事件類型可以是基本類型,String類型,或者自定義的JavaBean。

 EventBus.getDefault().post("Hello");

使用EventBus就是以上步驟,先註冊事件,然後發佈事件。在註冊事件的地方便能收到發佈事件時攜帶的數據。接下來我們就進入源碼分析。理解EventBus內部是如何工作。

三、源碼分析-註冊流程
3.1 EventBus#getDefault方法
  public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

EventBus首先通過getDefault方法獲取到單例的EventBus,該方法內做了雙重判斷,由於加了同步鎖會對性能產生影響,這樣做可以優化性能。接下來看EventBus的構造方法。

 public EventBus() {
        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;
    }

在構造方法內,通過EventBusBuilder類初始化了如下數據:

subscriptionsByEventType: Map<Class<?>, CopyOnWriteArrayList>對象。該Map以事件類型的Class對象爲key,以 CopyOnWriteArrayList集合爲value。Subscription保存了Activity實例,以及註解的方法名稱,以及事件類型的Class對象,及註解的值,使用CopyOnWriteArrayList保證了線程安全。

typesBySubscriber: Map<Object, List<Class<?>>>對象。該Map以Activity實例爲key,以事件類型的Class對象爲集合的value,組成映射。

**stickyEvents:**Map<Class<?>, Object>對象。存儲粘性事件的Map。以事件類型的Class對象爲key,以事件類型爲value組成映射。

3.2 EventBus#register方法

調用register方法,在該方法內獲取Activity實例的Class對象,然後調用SubscriberMethodFinder#findSubscriberMethods方法獲取SubscriberMethod方法的集合。然後遍歷調用EventBus#subscribe方法。

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
3.3 SubscriberMethodFinder#findSubscriberMethods方法

先從METHOD_CACHE這個map對象緩存中獲取泛型爲SubscriberMethod的集合。不爲空直接返回該對象。不爲空走以下邏輯。

由於ignoreGeneratedIndex初始化時false,因此調用SubscriberMethodFinder#findUsingInfo方法獲取到SubscriberMethod的集合,並添加到METHOD_CACHE緩存中。

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            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;
        }
    }
METHOD_CACHE

METHOD_CACHE是以Class對象爲key即存儲的是Activity實例的Class對象。以泛型是SubscriberMethod的集合爲value,那麼SubscriberMethod存儲的是什麼值呢。

Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
SubscriberMethod

SubscriberMethod類中存儲了接受事件的方法名稱的反射類型,線程模式,事件類型的Class對象,優先級,是否是粘性事件。

public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;
    ....
    }
3.4 SubscriberMethodFinder#findUsingInfo方法

先調用SubscriberMethodFinder#prepareFindState方法獲取FindState對象。然後將Activity的實例的Class對象緩存在FindState對象中。由於getSubscriberInfo方法返回爲null,所以最終調用findUsingReflectionInSingleClass方法並將FindState對象傳入。

 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }
3.5 FindState#prepareFindState方法

在 prepareFindState 方法中,FIND_STATE_POOL是FindState類型的,大小爲POOL_SIZE=4的長度數組,每次調用時,會找到這個數組內爲空的對象,並將數組內該索引清空,返回當前索引的對象,如果這個數組內爲空重新創建一個FindState對象。

private FindState prepareFindState() {
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
    }
3.6 FindState#initForSubscriber

將Actiivty實例的Class對象緩存在FindState內。

  void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }
3.7 SubscriberMethodFinder#findUsingReflectionInSingleClass

在該方法內通過反射獲取Activity的實例的Class對象的所有方法,並獲取方法上的註解值,並最終將方法,運行線程的類型等組裝到SubscriberMethod對象中,並保存在FindState中。

    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();
                   if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                       Class<?>[] parameterTypes = method.getParameterTypes();
                       if (parameterTypes.length == 1) {
                           Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                           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");
                   }
               }
           }
3.8 SubscriberMethodFinder#getMethodsAndRelease方法。

將保存在FindState對象中的SubscriberMethod拷貝一份,然後清空FindState內的緩存數據,。

  private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
               List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
               findState.recycle();
               synchronized (FIND_STATE_POOL) {
                   for (int i = 0; i < POOL_SIZE; i++) {
                       if (FIND_STATE_POOL[i] == null) {
                           FIND_STATE_POOL[i] = findState;
                           break;
                       }
                   }
               }
               return subscriberMethods;
           }
3.9 EventBbus#subscribe方法訂閱

將Activity的實例對象和 SubscriberMethod 對象保存在 Subscription 對象中。然後將註解方法的參數的Class對象與Subscription的集合形成映射,保存在 subscriptionsByEventType這個map中。然後按照事件設置的優先級大小對保存在Subscription集合中的信息排序。然後將Activity的實例對象與事件類型的Class對象的集合保存在 typesBySubscriber這個map 中。最後判斷該事件是否是粘性事件。

粘性事件與非粘性事件:指粘性事件可以先發送,後註冊,也會受到發送的信息,非粘性事件是必須先註冊事件,後發送。如果是粘性事件直接調用checkPostStickyEventToSubscription。然後調用postToSubscription這個發送的方法,關於postToSubscription方法稍後再發布分析中去解析。

 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);
            }
            subscribedEvents.add(eventType);
    
            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);
                }
            }
        }
   private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }
四、源碼分析-發佈流程
4.1 EventBus#post

發佈事件調用的是以下代碼,獲取EventBus的單例並調用post方法。

 EventBus.getDefault().post("Hello");

通過currentPostingThreadState這個ThreadLocal對象獲取存儲在內部的PostingThreadState對象。通過PostingThreadState獲取其內部的eventQueue集合。

判斷事件是否在發送,然後判斷是否是主線程,設置到PostingThreadState對象中,循環調用EventBus#postSingleEvent方法,直到eventQueue集合爲空。

/** 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 = isMainThread();
            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;
            }
        }
    }
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };
4.2 PostingThreadState

PostingThreadState是EventBus內部的一個類,存儲Subscription類以及事件類型對象及其他信息。上文提到Subscription中保存了註冊的Actvity的實例對象和存儲了事件類型的Class對象,註解值,及方法名的SubscriberMethod對象。

   final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<>();
        boolean isPosting;
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }
4.3 EventBus#postSingleEvent

在該方法中首先獲取事件類型的Class對象,接着調用EventBus#postSingleEventForEventType方法。將事件類型對象,以及PostingThreadState和事件類型的Class對象傳進去。

 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) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
4.4 EventBus#postSingleEventForEventType

在該方法內通過subscriptionsByEventType這個map獲取到Subscription對象的集合,subscriptionsByEventType這個map在上文的subcribe方法中提到存儲了事件類型的Class對象以及Subscription對象的集合。然後將事件類型對象和Subscription對象設置給PostingThreadState對象。並調用postToSubscription。

 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;
    }
4.5 EventBus#postToSubscription
  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 MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(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);
        }
    }

ThreadMode:POSTING,默認值,表示發送事件和接受事件在統一線程。在此狀態下調用EventBus#invokeSubscriber方法,在invokeSubscriber方法中通過反射調用保存Subscription對象中Activity實例中的註解方法。

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);
        }
    }

ThreadMode:MAIN,首先判斷是否是主線程,若是主線程直接調用,此種情況與ThreadMode爲POSTING一致。若不是則調用mainThreadPoster這個Poster#enqueue方法,具體實現是在EventBus初始化時創建Poster的實現類。

 mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;

在MainThreadSupport中的內部類AndroidHandlerMainThreadSupport中通過主線程的Looper對象創建HandlerPoster即是Handler類。

@Override
        public Poster createPoster(EventBus eventBus) {
            return new HandlerPoster(eventBus, looper, 10);
        }

最終調用HandlerPoster#enqueue方法,然後調用sendMessage方法,最終調用handleMessage方法,最終調用EventBus#invokeSubscriber方法,在invokeSubscriber方法中通過反射調用Subscription中保存的Activity實例的添加註解的方法。

其他幾種基本類似,就不一一說了,最終調用與其他兩種一致。

  • 總結
    EventBus通過反射處理註解,從註冊的實例的Class對象中獲取到註解方法的方法名,事件類型的Class對象及註冊的Activity實例等值,並保存起來,當調用post方法時,通過事件類型的Class對象獲取保存起來的對應的信息。並最終通過反射調用保存的Activity實例的註解方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章