EventBus內部是如何實現的? 讓我們愛不釋手

簡言:

相信大家都使用過EventBus,用起來真的是奧利給啊,使用簡單,開銷還小,這種異步框架真的是讓人愛不釋手啊,當然了,有一大部分人更加喜歡Rxjava這個框架,畢竟這個框架更NB, 但是今天得主角不是Rxjava,是EventBus,帶大家看看他的內部實現是什麼?當然在講源碼之前,還是先介紹一下EventBus,我保證,只是介紹啊, 畢竟我也不想囉嗦。。。

1.簡介
EvenBus是一個android端優化的消息總線,簡化了應用程序內各組件,組件與後臺線程間的通信,比如請求網絡,等網絡返回時通過Handler或者BroadCast通知UI,兩個Fragement之間需要通過listener通信,這些需求都可以通過Evenbus實現;
evenBus是一款針對 android 優化的發佈/訂閱事件總線。主要功能是代替Intent handler Broacaset 在fragement activity
service線程之間的傳遞消息,優點是開銷小,代碼更優雅,以及將發送者和接收者解耦.


2.基本的使用:

1,自定義一個類,可以是空類;
2.在要接收消息的頁面註冊
3.發送消息
4.接收消息的頁面實現(共有四個函數,各功能不同)
5.解除註冊
 

3.EventBus的流程 (如圖:官網找的)

publisher是一個發佈器,然後將我們的事件Event通過post()方法發送到EventBus的主線當中,然後在這個EventBus的主線中,會通過事件(Event)類型匹配相應的訂閱者 Subscriber。

4.源碼分析(EventBus)

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //發送消息
                EventBus.getDefault().post(new MyEvents("hello eventbus"));
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        //註冊EventBus
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        //註銷EventBus
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MyEvents events){
        Toast.makeText(this,events.message,Toast.LENGTH_SHORT).show();
    }

這個是Event的使用類,我們看在使用EventBus時,無論註冊還是註銷發送消息,都有getDefault()這個方法,所以我們先看看這個getDefault方法實現了什麼?

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

  public static EventBusBuilder builder() {
        return new EventBusBuilder();
    }

    /** For unit test primarily. */
    public static void clearCaches() {
        SubscriberMethodFinder.clearCaches();
        eventTypesCache.clear();
    }

    /**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

這裏沒有什麼可介紹的,這是一個單例模式,我們關注一下這個EventBus的構造函數,DEFAULT_BUILDER,我們跟進去看一下:

我們可以看出,這個EventBus最終是構建者模式來創建對象的。我們看看他是如何創建的,

   EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        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;
    }

 

  subscriptionsByEventType = new HashMap<>();
  typesBySubscriber = new HashMap<>();
  stickyEvents = new ConcurrentHashMap<>();

這三個HashMap,都是做什麼?

第一個:它是以Event爲key,subscript爲value,當發送Event時,都可以通過這個HashMap找到對應訂閱者。

第二個:它是以Subscriber爲key,event爲value,當我們做反註冊的時候都會操作這個hashMap

第三個:這個是維護的粘性事件,我之前講過粘性事件的定義,這裏就不追溯了。

  
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
  backgroundPoster = new BackgroundPoster(this);
  asyncPoster = new AsyncPoster(this);

我們在看看這三個post(重要)

1)HandlerPoster:我們看參數,傳遞的是主線程的Looper,是handler現實的,我們追進去看看handerPoster做了什麼?

final class HandlerPoster extends Handler {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

這個HandlerPoser繼承Handler,

   PendingPostQueue queue;  這是一個隊列
   int maxMillisInsideHandleMessage; post這個事件在hanlder中最大的時間值
    EventBus eventBus;
   handlerActive; 他標識的是是否運行起來了

我們關注一下handleMessage方法做了什麼?

通過do,while這個循環,從隊列中獲取數據,並調用eventBus,invokeSubscriber()分發事件,每分發完一次事件,就對比一下時間,判斷這個時間,與上邊定義的最大時間值,做比較,如果大於就跳出循環。

2)backgroundPoster (處理後臺操作的)

final class BackgroundPoster implements Runnable {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

他實現的是Runnalbe,我們直接看他的run()方法:run()主要是不斷的從我們隊列中獲取消息,然後通過invokeSubscruber進行事件分發。直到取完爲止。

3)asyncPoster

class AsyncPoster implements Runnable {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

}

它也是實現的Runnable,

他的run方法,是獲取隊列中的一個,進行事件分發,與上邊的post是不同的,

 

上邊三個post是EventBus中最核心的類,

 

3.subscriberMethodFinder(這個也是重要的,他是註解的找尋器)

 

二,關於註解的分析

  @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MyEvents events){
        Toast.makeText(this,events.message,Toast.LENGTH_SHORT).show();
    }

我們還是看一下@Subscribe的源碼:

@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 線程模式。這個是非常重要的,我們分析一下:


public enum ThreadMode {
   
    POSTING,
     
   
    MAIN,


    BACKGROUND,


    ASYNC
}

1)POSTING:一種默認線程模式,表示在執行post事件操作的時候線程直接調訂閱者的方法,無論該線程是否在主線程。

2)MAIN:表示在主線程中執行這個方法,

3)BACKGROUND:在後臺線程中執行相應的方法。

4)ASYNC:無論發佈的是否在主線程。它都會發佈一個空線程進行處理。它線程獨立,不會出現卡頓。

2.sticky: 這是定義的粘性事件,

3.priority: 優先級,默認情況下是POSTING.

 

三,register() 註冊訂閱

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

第一行代碼:通過反射獲取到我們的subscriberClass對象,

第二行代碼:通過我們獲取到的對象找到對應的集合。subscriberMethodFinder這個找尋器進行尋找。

我們看一下findSubscriberMethods這個方法源碼:

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

我們看一下findUsingInfo這個方法的源碼:

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

while這個循環,通過getSubScriberInfo這個方法返回的不爲null,

首先獲取訂閱方法的集合,通過for循序遍歷我們的訂閱方法,並通過checkAdd這個方法進行過濾,將符合的添加到subscriberMethods這個集合中。

如果etSubScriberInfo這個方法返回的爲null,會走findUsingReflectionInSingleClass這個方法:我們通過命名就和以猜出,他是通過反射進行查詢的。我們看一下源碼:

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

   methods = findState.clazz.getDeclaredMethods(); 通過反射做的,我們猜的沒錯,這裏就獲取到所有訂閱者的方法,

然後對獲取的方法進行了依次的遍歷,通過getParameterTpyes方法獲取到我們的參數,通過getAnnotation這個方法獲取到我們的Subscribe對象,這裏其實主要做的就是過濾出被Subscribe修飾過的方法,subscribeAnnotation.threadMode,獲取線程模式,通過這個線程模式進行調度,

2.我們繼續分析register 中的subscribe

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

Subscription 這個類是做什麼的?我們看一下:

final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
    /**
     * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
     * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
     */
    volatile boolean active;

    Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
        this.subscriber = subscriber;
        this.subscriberMethod = subscriberMethod;
        active = true;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Subscription) {
            Subscription otherSubscription = (Subscription) other;
            return subscriber == otherSubscription.subscriber
                    && subscriberMethod.equals(otherSubscription.subscriberMethod);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
    }
}

這裏處理了訂閱者,和封裝的一些訂閱方法,線程模式,等,

我們回去繼續看:

subscriptions這個爲null.證明這個事件還沒有註冊過,我們就新創建一個CopyOnWriteArrayList,並添加到SubScriptByEventType這個hashMap當中,

subscriptions爲null的話就會拋出異常,證明這個時間已經註冊過了。

subscriptions.size回去這個集合大小,

通過for循環遍歷,通過優先級的條件添加到subscriptions中。

這裏對粘性事件也做了處理,這裏不講粘性事件,

最後    checkPostStickyEventToSubscription(newSubscription, stickyEvent);完成了註冊。

 

總結:subscribe

1.判斷是否有註冊過該事件

2.然後按照優先級加入到SubscriptionByEventType的value的list中

3.然後再添加到typesBySubscriber的value的List中。

4.分發事件

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章