Skip to content

Latest commit

 

History

History
367 lines (275 loc) · 16.2 KB

eventbus.md

File metadata and controls

367 lines (275 loc) · 16.2 KB

EventBus源码分析

使用 EventBus 时注意

  1. 使用 EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();才是开启注解方式 eventBus.register(this);

  2. 而使用 EventBus.getDefault().register() 是使用反射的方式

1.第一步:编译时期注解解析器

** 编译时 主要生成MyIndexEvent类 **

/** This class is generated by EventBus, do not edit. */

    public class MyEventBusIndex implements SubscriberInfoIndex {
        private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
    
        static {
            SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.TestEventBusActivity.class, true,
                    new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onPostTest", org.greenrobot.eventbusperf.testsubject.MEvent.class,
                        ThreadMode.MAIN),
            }));
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscriberClassEventBusAsync.class,
                    true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onEventAsync", TestEvent.class, ThreadMode.ASYNC),
            }));
    
    
    
        private static void putIndex(SubscriberInfo info) {
            SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
        }
    
        @Override
        public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
            SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
            if (info != null) {
                return info;
            } else {
                return null;
            }
        }
    }

2.EventBus 构造方法

Step1:EventBus .getDefault() 创建了一个单例,采用双校验方式 Step2: 调用EventBus构造方法创建4个集合,集合的命令通过 dataByKey的形式

      //以事件类型作为Key,Subscription的List集合作为Value的Map集合,后面post一个事件也就用到这个集合
      
      1. Map<Class<?>,CopyOnWriteArrayList<Subscription>> subscriptionsByEventType = new HashMap<>();
      //订阅者作为Key,订阅事件作为Value的Map集合
      
      2 Map<Object, List<Class<?>>> typesBySubscriber = new HashMap<>();

      3.Map<Class<?>, Object> stickyEvents== new ConcurrentHashMap<>();
      4.Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();

      4.创建EventBusBuilder :
      5.创建一个BackgroundPoster:
      创建一个 HandlerPoster extends Handler implements Poster

EventBusBuilder构建构造方法中 创建三个集合

    1.创建一个线程池  ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
    2.List<Class<?>> skipMethodVerificationForClasses;
    3.List<SubscriberInfoIndex> subscriberInfoIndexes;
    MainThreadSupport mainThreadSupport;

3. register方法干了什么?

对于register中的参数,就是我们的订阅者,也就是我们经常传入的this对象。在这个方法中主要完成了两件事情。首先通过findSubscriberMethods方法来查找订阅者中所有的订阅方法。然后遍历订阅者的订阅方法来完成订阅者的订阅操作。

首先在这里来看一下findSubscriberMethods这个方法是如何查找订阅者的订阅方法。在这先描述一下SubscriberMethod类。对于SubscriberMethod类中,主要就是用保存订阅方法的Method对象,线程模式,事件类型,优先级,是否粘性事件等属性。下面就来看一下findSubscriberMethods方法。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //从缓存中获取SubscriberMethod集合
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    //ignoreGeneratedIndex是否忽略注解器生成的MyEventBusIndex
    if (ignoreGeneratedIndex) {
        //通过反射获取subscriberMethods
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        //通过注解器生成的MyEventBusIndex信息获取subscriberMethods,
        //如果没有配置MyEventBusIndex,依然通过通过反射获取subscriberMethods
        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;
    }
}




private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    //创建和初始化FindState对象
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        //获取订阅者信息,没有配置MyEventBusIndex返回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();
    }
    //回收处理findState,并返回订阅方法的List集合
    return getMethodsAndRelease(findState);
}


private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //获取订阅方法中的订阅事件
    Class<?> eventType = subscriberMethod.eventType;
    //创建一个Subscription来保存订阅者和订阅方法
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //获取当前订阅事件中Subscription的List集合
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
    //该事件对应的Subscription的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);
        }
    }

    //将newSubscription按照订阅方法的优先级插入到subscriptions中
    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中
    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);
        }
    }
}


在这个方法中便是订阅者真正的注册过程。首先会根据subscriber和subscriberMethod来创建一个Subscription对象。之后根据事件类型获取或创建一个Subscription集合subscriptions并添加到typesBySubscriber对象中。最后将刚才创建的Subscription对象添加到subscriptions之中。于是这样就完成了一次订阅者的注册操作

4.Post 方法 :

  1. 通过ThreadLocal获取各自线程的PostingThreadState,所以同一线程使用同一个eventQueue

  2. PostingThreadState: 保存正在被邮递的事件

  3. 将event 添加到PostingThreadState的eventQueue

  4. 判断是否正在邮递,因为每个线程只有一个PostingThreadState,当前线程只能有一个在处理 开始邮递

  5. 判断当前线程是否是主线程:postingState.isMainThread

  6. 通过事件类型查找所有方法 List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);

  7. postSingleEventForEventType

    // 事件类型 查找 订阅者 Subscription CopyOnWriteArrayList subscriptions; synchronized (this) { subscriptions = subscriptionsByEventType.get(eventClass); }

    Subscription {

         Object    包含订阅者对象subscriber  
    
         SubscriberMethod   订阅者订阅方法,注解参数
    

    }

  8. postToSubscription(Subscription subscription, Object event, boolean isMainThread) 将事件邮递给 邮递给订阅者 isMainThread 参数是判断当前是否是主线程

  9. 如果是主线程 直接调用 invokeSubscriber(subscription, event) 直接通过反射 调用.invoke传入对象和参数 subscription.subscriberMethod.method.invoke(subscription.subscriber, event);

     private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    
     Log.i("qy","postToSubscription处理线程切换问题");
     switch (subscription.subscriberMethod.threadMode) {
         case POSTING:
             // 不切换线程,post和订阅者方法在同一个线程执行
             invokeSubscriber(subscription, event);
             break;
         case MAIN:
             Log.i("qy","订阅方法需要在主线程");
             if (isMainThread) {
                 Log.i("qy","post方法在主线程,直接反射去掉用");
                 invokeSubscriber(subscription, event);
             } else {
                 Log.i("qy","post方法在子线程,通过Handler切换到主线程");
                 mainThreadPoster.enqueue(subscription, event);
             }
             break;
         case MAIN_ORDERED:
             Log.i("qy","订阅方法需要在主线程排队执行");
             if (mainThreadPoster != null) {
                 Log.i("qy","开始排队执行");
                 mainThreadPoster.enqueue(subscription, event);
             } else {
                 Log.i("qy","反射立刻执行");
                 // temporary: technically not correct as poster not decoupled from subscriber
                 invokeSubscriber(subscription, event);
             }
             break;
         case BACKGROUND:
             Log.i("qy","订阅者需要再 后台执行 BACKGROUND,可以允许排队执行");
             if (isMainThread) {
                 Log.i("qy","Post在主线程,所以喜欢转换子线程执行,并且排队执行");
                 backgroundPoster.enqueue(subscription, event);
             } else {
                 Log.i("qy","post在子线程,所以可以立即执行");
                 invokeSubscriber(subscription, event);
             }
             break;
         case ASYNC:
             Log.i("qy","订阅者需要再 后台执行 不需要排队,直接交给线程池异步执行");
             asyncPoster.enqueue(subscription, event);
             break;
         default:
             throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
     }
    

    }

5. 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 {
        logger.log(Level.WARNING, "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--;
                }
            }
        }
    }

也就是说,unregister 先通过传过来的订阅者找个所有订阅事件类型,比如一个LoginActivity订阅者,里面有很多有参数为 FindEvent LoginEvent 等等,这里也就是先前在订阅的时候保存的集合typesBySubscriber开始派上用场了,接着 通过事件类型 去subscriptionsByEventType 查找所有订阅者,并且判断订阅者等于当前LoginActivity这个,然后remove,同时也要typesBySubscribler 也要remove

技术点:ThreadLocal保证多线程不干扰,

CopyOnWriteArrayList 保证多线程安全,提高效率,通过复制,改变引用的方式, 双校验单例模式并使用voliate 阻止重排序问题

BackgroundPoster:后台发布人

     private final PendingPostQueue queue;
     private final EventBus eventBus;
     private volatile boolean executorRunning;

       BackgroundPoster implements Runnable, Poster

  enqueue 方法:
     1. 从pendingPostPool中获取一个PendingPost 对象并赋予新的属性值
     2.将PendingPost添加到PendingPostQueue队列,这是一个同步代码块
      3.判断一下线程池是否运行,运行就等待,否者就

Poster 发布人 void enqueue(Subscription subscription, Object event);

PendingPost 是一个链表,具有3个属性,事件,订阅者和next,pendingPostPool用来作为PendingPost缓存,也就是

private final static List pendingPostPool = new ArrayList();

Object event; Subscription subscription; PendingPost next;