Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> EventBus 源碼分析

EventBus 源碼分析

編輯:關於Android編程

先上github 地址:eventbus

首先,說說Eventbus 是什麼,能干什麼

其為android中的事件總線框架,用於在app內不同模塊間通信消息傳遞的框架。簡單的說,就是一個地方發送一個廣播,在注冊了該廣播的地方接受到廣播(在對應的函數中)。

怎麼使用?

1.需要創建一個類,該類對應著一類事件。(相當於廣播中的,意圖過濾器中的條件)

class MyEvent{} // 代表一類事件。 即一類的廣播。

2.在需要相應的類中,進行注冊。(對應的要在該類銷毀的時候,進行注銷操作)

EventBus.getDefault().register(this);// 注冊該類為接受者
EventBus.getDefault().unregister(this);// 注銷該類。

3.在注冊的類中,添加對應接受事件的函數。比如

public void onEvent(MyEvent event) {
// TODO 在接收到該事件時,我們要進行的相應處理。
}

4.發送一個事件。也可以理解為發送一個廣播。

EventBus.getDefault().post(new MyEvent());//  發送的MyEvent對象,會被注冊了該事件的方法中得到回調。

簡單的用法就是這些了。

eventbus 源碼

我們就從其暴露給我們的接口來一步步看起源碼。

首先看從getDefault方法開始。

// 這裡可以看到一個典型的創建的單例
public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
  }

在eventbus的構造函數中

EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap, CopyOnWriteArrayList>();//   key 為所以Event的class,value為          該注冊了該event的所以類。
        typesBySubscriber = new HashMap

然後我們在看一下register(Object subscriber)方法,都會調用一個方法。

//subscriber 為注冊的類,sticky 是否是粘性事件,priority為優先級 ,會影響存入map的順序,進而影響回調的順序。
private synchronized void register(Object subscriber, boolean sticky, int priority) { 

        List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

首先,我們看到首先調用了SubscriberMethodFinder的findSubscriberMethods方法。給類做的事情就是 把 訂閱了事件的類中的所有的onEvent回調函數給返回出去。

List findSubscriberMethods(Class subscriberClass) {
        String key = subscriberClass.getName();
        List subscriberMethods; 
        synchronized (methodCache) {
            subscriberMethods = methodCache.get(key);// methodCache 為一個static變量,注冊的類為key,以該類中所有注冊函數的list為value,相當於一級緩存。將所有的注冊類中的注冊方法保存下來。避免多次遍歷類中方法的操作。
        }
        if (subscriberMethods != null) {
            return subscriberMethods; // 如果已經處理過該類,直接使用緩存下來的數據返回。
        }
        subscriberMethods = new ArrayList();
        Class clazz = subscriberClass;
        HashSet eventTypesFound = new HashSet(); // 保存已經添加的方法和該方法的參數拼接的字符串,用於防止重復添加。
        StringBuilder methodKeyBuilder = new StringBuilder();
        while (clazz != null) { // 循環遍歷該類,以及其父類
            String name = clazz.getName();
            //忽略掉系統中的類
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                // Skip system classes, this just degrades performance
                break;
            }
            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                String methodName = method.getName();
                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { // 以OnEvent開始的方法
                    int modifiers = method.getModifiers();        
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //訪問修飾符判斷 public且非Ignone方法
                        Class[] parameterTypes = method.getParameterTypes();
                        if (parameterTypes.length == 1) {//  只有一個參數的方法
                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
                            ThreadMode threadMode;
                            if (modifierString.length() == 0) {
                                threadMode = ThreadMode.PostThread;
                            } else if (modifierString.equals("MainThread")) {
                                threadMode = ThreadMode.MainThread;
                            } else if (modifierString.equals("BackgroundThread")) {
                                threadMode = ThreadMode.BackgroundThread;
                            } else if (modifierString.equals("Async")) {
                                threadMode = ThreadMode.Async;
                            } else {
                                if (skipMethodVerificationForClasses.containsKey(clazz)) {
                                    continue;
                                } else {
                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);
                                }
                            }
                            Class eventType = parameterTypes[0];
                            methodKeyBuilder.setLength(0);
                            methodKeyBuilder.append(methodName);
                            methodKeyBuilder.append('>').append(eventType.getName());
                            String methodKey = methodKeyBuilder.toString();
                            if (eventTypesFound.add(methodKey)) {  
                                // Only add if not already found in a sub class SubscriberMethod類為封裝了一個類中接收函數信息的封裝類。其中包含了方法method、該方法需要回調的線程threadmode、對應的事件類型eventtype。
                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));

                            }
                        }
                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
                                + methodName);
                    }
                }
            }
            clazz = clazz.getSuperclass(); // 獲取父類的class,繼續循環
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            synchronized (methodCache) {
                methodCache.put(key, subscriberMethods);//將該類中的所有對應的響應方法的集合緩存下來。
            }
            return subscriberMethods;
        }
    }

然後,遍歷得到集合,調用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)方法。該方法 主要功能就是為了將我們在EventBus 構造函數中創建的subscriptionsByEventType、typesBySubscriber和stickyEvents這三個集合,將對應的數據保存在他們中。前面已經說過他們對應保存的數據,這裡就不重復了。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
        Class eventType = subscriberMethod.eventType; // 事件的對應的class
        CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); // 先從集合中獲取以前保存的。
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);// subscription 類為對接受事件的類的包裝類,裡面包含 subscribe        r 就是我們register(Object o)時傳入的this。一般就是activity或fragment。subscribermethod 就是該類中的onevent方法的封裝,priority
        if (subscriptions == null) {//如果為空,代表第一次處理該event。創建list,並且將list添加到subscriptionsByEventType中。
            subscriptions = new CopyOnWriteArrayList();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        //將newSubscription按照優先級添加到中。
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }// 到此,subscriptionsByEventType這個集合填充完畢。
        List> subscribedEvents = typesBySubscriber.get(subscriber);//同樣的,先將這個類中之前event.class的集合取出。
        if (subscribedEvents == null) {  // 如果為空,代表是第一次,創建list集合,並且將其放到typesBySubscriber這個map中
            subscribedEvents = new ArrayList>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);// 將event加到剛才創建的list中。
        if (sticky) {  // 是否是粘性事件。如果不是的話,register方法到此就結束了。如果是注冊的是粘性事件監聽,就會在注冊的時候,執行以下代碼。
            Object stickyEvent;
            synchronized (stickyEvents) {
                stickyEvent = stickyEvents.get(eventType); // 該集合中的stickyEvent是在postSticky(Object o)的時候,添加到集合中的。所以所謂的粘性事件就是在這類                                                           注冊粘性事件監聽之前,就已經被post過的事件,在注冊的時候會被直接調用,並將之前的Object作為參數傳入。
            }
            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, Looper.getMainLooper() == Looper.myLooper()); // 這裡是post粘性事件。該方法是post事件的核心方                法。之後在分析post模塊的時候,會繼續分析。
            }
        }
    }

到這裡,我們就算走完了全部register的流程。我們現在回顧一下注冊所做的事情。其實,最主要的事情就是將注冊的類(比如activity)中的對應的onevent回調回調函數(比如 OnEvent(MyEvent myevent)),以及其參數,通過自己定義的包裝類,保存到相應的集合之中(subscriptionsByEventType,typesBySubscriber)。

然後,我們再看一看,我們是如何post一個事件是如何實現的。

public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();//獲得在和該線程有關的postingState對象。
        List

首先,我們先看一下PostingThreadState這個類。它是EventBus的內部類。其中主要是保存事件的一些信息,就是一個bean對象。這裡需要特別關注一下currentPostingThreadState這個對象。它是ThreadLocal的實例。ThreadLocal可以理解一個用於在不同線程中保存數據的一個容器。比如你在主線程中添加了一條數據,又在子線程中添加了一條數據,然後,當你在主線程中去get的時候,拿到的就是你之前在主線程中保存的數據,如果你在子線程中去get,那麼拿到的就是你在子線程中保存的數據。對於具體如何實現的,有興趣的同學可以自己去研究研究,其中有用到很巧妙的算法。

然後我們在看看postSingleEvent(Object event, PostingThreadState postingState)方法.

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class eventClass = event.getClass(); // event對應的class
        boolean subscriptionFound = false;  
        if (eventInheritance) {   // 代表是否將event的父類,也進行處理。
            List> eventTypes = lookupAllEventTypes(eventClass);  // 在該方法中,通過循環,將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);
        } 
}

然後,其都會調用postSingleEventForEventType方法,返回的boolean代表是否存在對應的注冊者類。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
        CopyOnWriteArrayList subscriptions;
        synchronized (this) { // 這裡我們看到,我們通過eventClass從subscriptionsByEventType中拿到我們之間在注冊的時候添加進去的該event的所對應的注冊者的集合。
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {// 遍歷該event 所有的注冊者
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;  // 將信息保存到postingState對象中
                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;
    }

在這裡然後調用 postToSubscription方法。該方法在之前就有遇到過。實在register時,對於粘性事件我們調用了該方法。現在我們就來看看到底該方法干了什麼。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case PostThread:
                invokeSubscriber(subscription, event);
                break;
            case MainThread:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BackgroundThread:
                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);
        }
    }

我們看到了,主要是通過區分subscription.subscriberMethod.threadMode的類型,以及當前方法執行的線程(也就是post方法調用的時候的線程),來調用不同方法。

這裡主要有四種方式。 一種是invokeSubscriber(subscription, event) (其實最後調用的都是這個方法,不過是在不同線程中調用而已)代表在當前線程中調用。第二種 mainThreadPoster.enqueue(subscription, event),在主線程中調用。第三種 backgroundPoster.enqueue(subscription, event) 會在子線程中調用,而且該線程是維護了一個隊列,依次處理所有的加入的數據。第四種是asyncPoster.enqueue(subscription, event)會開辟新的子線程在其中執行。

正如我剛才說的,其實最後調用的都是invokeSubscriber(Subscription subscription, Object event)這個方法。 只是通過在不同線程中調用,來實現在不同線程中回調。

void invokeSubscriber(Subscription subscription, Object event) {
      subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
     // 實現功能的就是該方法,通過反射調用該方法。 
     .......
    }

現在我們來看看如何實現在不同線程中調用該方法。如果還記得的話,我們在eventbus的構造函數中,曾經創建過三個對象

mainThreadPoster,backgroundPoster,asyncPoster這三個對象,對應於不同線程。

首先,mainThreadPoster其實就是一個handler,而且是主線程中的handler,所以其對應的handleMessage方法會在主線程中執行。

首先調用了enqueue(Subscription subscription, Object event)方法。

首先,我們看PendingPost類,該也是一個封裝event和subscription的類,但是不同的是其中包含一個pendingPostPool的存儲集合,該集合裡面添加進去的是我們之前創建過的PendingPost的對象。其作用是復用以前創建過的對象,減少對象創建的開銷。

static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;       // 將使用完畢的PendingPost中的數據清空
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost); 添加到集合中
            }
        }
    }

在我們需要一個新的PendingPost的對象的時候

static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) { //如果集合中有不用的PendingPost,將其取出,並且將對應的數據添加到其中。
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

然後,回到enqueue方法中,

void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); // 獲得封裝了subscription和event的pendingpost對象
        synchronized (this) {
            queue.enqueue(pendingPost); // 將其添加到自己定義的隊列中
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) { // 調用sendmessage方法發送一個空消息,會調用handler的handlemessage方法中。
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

然後,來到handlemessage方法

public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis(); //獲取系統時間,相當於進入循環是的時間,因為我們看到下面是一個死循環,會用這個時間做一個限制。
            while (true) {
                PendingPost pendingPost = queue.poll();  //從隊列中取出在enqueue中添加的數據。
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }  // 以上部分是為了判斷隊列是否為空的考慮線程安全的方法。
                eventBus.invokeSubscriber(pendingPost); // 這裡,就是實現真正功能的代碼。我們看到,它又回到了EventBus類中,只不過,它是在主線程中調用的。因為這是在主線程的handler的handlemessage方法中。
                long timeInMethod = SystemClock.uptimeMillis() - started; //又獲取了一次系統時間,通過時間差,來控制循環。(當隊列中數據好多時,才會用到)
                if (timeInMethod >= maxMillisInsideHandleMessage) {   
                    if (!sendMessage(obtainMessage())) {  //如果此次循環已經超過規定時間,發送一個消息,會重新調用handlemessage方法。
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

我們再回到eventbus中看看invokeSubscriber方法

void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription; // 將封裝在PendingPost中的數據取出來。
        PendingPost.releasePendingPost(pendingPost);// 會將PendingPost對象中的數據清空,放到其內部的集合中,以便將來再用。
        if (subscription.active) {
            invokeSubscriber(subscription, event);// 該方法,以前已經分析過了,就是通過反射回調到相應的方法中,完成post的實現。
        }
    }

到這裡,我們就看完了在主線程中實現回調的實現了。對於BackgroundPoster和AsyncPoster其實現是類似的,唯一的區別就是其是實現Runnable接口,其是運行在子線程中的。其中也是先將數據添加到一個隊列中,然後再其run方法中調用eventBus.invokeSubscriber(pendingPost)方法。這兩個的區別就是 一個線程安全,一個線程不安全。

到此。post方法我們也算分析完畢了。對於unregister()方法,如果前面的都看懂了,那麼unregister就很簡單了,就是將之前register時保存到集合中的數據刪除掉就好了。這裡就不再累贅了。相信大家都可以輕松看懂的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved