EventBus源码解析

Posted 许佳佳233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EventBus源码解析相关的知识,希望对你有一定的参考价值。

概述

本篇内容适合对EventBus有兴趣,或者已经对EventBus有一定使用经验的读者。
如果读者此前还没有具体使用过EventBus,推荐阅读下笔者前文:

android为什么要用EventBus

register

关键逻辑:

  • 遍历当前注册的类,获取其中使用了eventBus注解的方法。

  • 将这些方法都注册到两个HashMap中,分别是subscriptionsByEventType和typesBySubscriber。通过synchronized加锁,来保证线程安全。

  • subscriptionsByEventType:key是eventType,value是List
    typesBySubscriber:key是注册的对象,value是List

  • 方法执行的时候,会去判断下是否有stcky事件,如果有的话会直接触发。

    public void register(Object subscriber) 
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) 
            for (SubscriberMethod subscriberMethod : subscriberMethods) 
                subscribe(subscriber, subscriberMethod);
            
        
    
    // 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);
            
        
    

post

关键逻辑:

  • post源码做了两件事,第一件事是将此时的event加入到eventQueue中。第二件事是将当前线程改成posting状态。
  • posting状态会去循环处理eventQueue中的event,将其放到对应的subscription中执行。
    – 查找subscription的过程是:先找到与event相关的类,然后遍历这些类与其相关的subscription。
    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;
            
        
    
    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));
            
        
    
    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 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;
    

postSticky

关键逻辑:

  • 与post相比,postSticky会先将event加入到stickyEvents这个Map中。
  • 在前面register中的逻辑已经讲到,当一个类注册的时候,会去判断是否有stickyEvent,如果有的话会直接触发。
    public void postSticky(Object event) 
        synchronized (stickyEvents) 
            stickyEvents.put(event.getClass(), event);
        
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    

多线程的逻辑

关键逻辑:

  • 在最终触发subscription时,会在postToSubscription中选择执行的线程。
  • POSTING: 直接在当前线程触发
  • MAIN:如果当前是主线程,那么直接触发。如果当前不在主线程,那么会通过handler抛到主线程去执行。
  • BACKGROUND:如果当前是子线程,那么直接触发。如果当前不在子线程,那么会抛到eventBus的线程池中执行。
  • ASYNC:抛到eventBus的线程池中执行。
    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);
        
    
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;
        
    


以上是关于EventBus源码解析的主要内容,如果未能解决你的问题,请参考以下文章

EventBus源码解析

EventBus设计之禅

ViewRootImpl源码解析 - 事件分发

ViewRootImpl源码解析 - 事件分发

ViewRootImpl源码解析 - 事件分发

ViewRootImpl源码解析 - View的更新