zoukankan      html  css  js  c++  java
  • EventBus3.0源码学习(1) register和unregister

    引子

    EventBus提供了一套完善的总线机制,能有效地用于模块间解耦。要使用EventBus,在项目模块的build.gradle中添加如下依赖即可:

    dependencies {
        compile 'org.greenrobot:eventbus:3.0.0'
    }

    类定义

     1 public class EventBus {
     2 
     3     static volatile EventBus defaultInstance;
     4 
     5     private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
     6     private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
     7 
     8     private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
     9     private final Map<Object, List<Class<?>>> typesBySubscriber;
    10     private final Map<Class<?>, Object> stickyEvents;
    11 
    12     private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    13         @Override
    14         protected PostingThreadState initialValue() {
    15             return new PostingThreadState();
    16         }
    17     };
    18 
    19     private final HandlerPoster mainThreadPoster;
    20     private final BackgroundPoster backgroundPoster;
    21     private final AsyncPoster asyncPoster;
    22     private final SubscriberMethodFinder subscriberMethodFinder;
    23     private final ExecutorService executorService;
    24 
    25     private final boolean throwSubscriberException;
    26     private final boolean logSubscriberExceptions;
    27     private final boolean logNoSubscriberMessages;
    28     private final boolean sendSubscriberExceptionEvent;
    29     private final boolean sendNoSubscriberEvent;
    30     private final boolean eventInheritance;
    31 
    32     private final int indexCount;
    33 
    34     public static EventBus getDefault() {
    35         if (defaultInstance == null) {
    36             synchronized (EventBus.class) {
    37                 if (defaultInstance == null) {
    38                     defaultInstance = new EventBus();
    39                 }
    40             }
    41         }
    42         return defaultInstance;
    43     }
    44 
    45     public EventBus() {
    46         this(DEFAULT_BUILDER);
    47     }
    48 
    49     EventBus(EventBusBuilder builder) {
    50         subscriptionsByEventType = new HashMap<>();
    51         typesBySubscriber = new HashMap<>();
    52         stickyEvents = new ConcurrentHashMap<>();
    53         mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
    54         backgroundPoster = new BackgroundPoster(this);
    55         asyncPoster = new AsyncPoster(this);
    56         indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    57         subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
    58                 builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    59         logSubscriberExceptions = builder.logSubscriberExceptions;
    60         logNoSubscriberMessages = builder.logNoSubscriberMessages;
    61         sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    62         sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    63         throwSubscriberException = builder.throwSubscriberException;
    64         eventInheritance = builder.eventInheritance;
    65         executorService = builder.executorService;
    66     }
    67 }

    注册(register)

    当一个对象想要从EventBus接收事件时,只需在EventBus中进行注册自己,调用EventBus.getDefault().register(this)即可。register的实现如下:

     1 public void register(Object subscriber) {
     2     Class<?> subscriberClass = subscriber.getClass();
     3     List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
     4     synchronized (this) {
     5         for (SubscriberMethod subscriberMethod : subscriberMethods) {
     6             subscribe(subscriber, subscriberMethod);
     7         }
     8     }
     9 }

    第3行,查找subscriber对应的类中所有接收EventBus事件的函数,然后依次调用subscribe函数注册这些所有查到的函数。subscribe的代码如下:

     1 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
     2     Class<?> eventType = subscriberMethod.eventType;
     3     Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
     4     CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
     5     if (subscriptions == null) {
     6         subscriptions = new CopyOnWriteArrayList<>();
     7         subscriptionsByEventType.put(eventType, subscriptions);
     8     } else {
     9         if (subscriptions.contains(newSubscription)) {
    10             throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
    11                     + eventType);
    12         }
    13     }
    14 
    15     int size = subscriptions.size();
    16     for (int i = 0; i <= size; i++) {
    17         if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
    18             subscriptions.add(i, newSubscription);
    19             break;
    20         }
    21     }
    22 
    23     List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    24     if (subscribedEvents == null) {
    25         subscribedEvents = new ArrayList<>();
    26         typesBySubscriber.put(subscriber, subscribedEvents);
    27     }
    28     subscribedEvents.add(eventType);
    29 
    30     if (subscriberMethod.sticky) {
    31         if (eventInheritance) {
    32             // Existing sticky events of all subclasses of eventType have to be considered.
    33             // Note: Iterating over all events may be inefficient with lots of sticky events,
    34             // thus data structure should be changed to allow a more efficient lookup
    35             // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
    36             Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
    37             for (Map.Entry<Class<?>, Object> entry : entries) {
    38                 Class<?> candidateEventType = entry.getKey();
    39                 if (eventType.isAssignableFrom(candidateEventType)) {
    40                     Object stickyEvent = entry.getValue();
    41                     checkPostStickyEventToSubscription(newSubscription, stickyEvent);
    42                 }
    43             }
    44         } else {
    45             Object stickyEvent = stickyEvents.get(eventType);
    46             checkPostStickyEventToSubscription(newSubscription, stickyEvent);
    47         }
    48     }
    49 }

    第3行,subscriber和subscriberMethod被封装成一个Subscription对象,Subscription描述了subscriber通过subscriberMethod对EventBus的一个事件订阅。

    第4-21行,subscriptionsByEventType存储了<eventType, subscriptions>的映射关系,当分发事件时,使用该Map可以快速找到订阅了该事件的所有对象及其用于处理该事件的方法。subscriptions中的subscription,可能来自不同类的对象、同一类的不同对象、甚至同一个对象的不同方法,subscriptions以subscriberMethod.priority维护它们的降序顺序。对同一个对象的重复注册会导致第10行抛出EventBusException异常。

    第23-28行,typesBySubscriber存储了<subscriber, subscribedEvents>的映射关系,当取消注册时,使用该Map可以快速找到该对象订阅的所有事件,或 判断该对象是否已经注册过了

    第30-48行,stickyEvents存储了<eventType, stickyEvent>的映射关系,与普通事件不同,除非显示移除,否则EventBus中总是保留该类事件的最近一次事件实例。当subscriberMethod.sticky为true,从stickyEvents中查找eventType对应的stickyEvent发送给该对象。若eventInheritance为true,查找stickyEvents中所有eventType及其子类的事件进行发送。

    取消注册(unregister)

    当一个类不再需要接收EventBus时,它需要在EventBus中取消注册,即调用EventBus.getDefault().unregister(this)。这很重要,在实际开发中,很多内存泄漏问题都是由于注册之后没有取消注册导致的。unregister的实现如下:

     1 public synchronized void unregister(Object subscriber) {
     2     List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
     3     if (subscribedTypes != null) {
     4         for (Class<?> eventType : subscribedTypes) {
     5             unsubscribeByEventType(subscriber, eventType);
     6         }
     7         typesBySubscriber.remove(subscriber);
     8     } else {
     9         Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    10     }
    11 }

    使用typesBySubscriber查找subscriber订阅了的所有事件类型,依次调用unsubcribeByEventType函数取消subscriber对每个eventType的订阅,然后将subscriber从typesBySubscriber中移除。unsubcribeByEventType的实现如下:

     1 private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
     2     List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
     3     if (subscriptions != null) {
     4         int size = subscriptions.size();
     5         for (int i = 0; i < size; i++) {
     6             Subscription subscription = subscriptions.get(i);
     7             if (subscription.subscriber == subscriber) {
     8                 subscription.active = false;
     9                 subscriptions.remove(i);
    10                 i--;
    11                 size--;
    12             }
    13         }
    14     }
    15 }

    使用subscriptionsByEventType查找eventType对应的subscriptions,然后移除subscriptions中所有subscription.subscriber==subscriber的subscription,并将subscription.active置为false。

    总结

    EventBus中与事件分发相关的底层实现主要涉及三个Map:

    • subscriptionsByEventType--<eventType, subscriptions>,主要用于事件分发,用于快速找到event的订阅者集合;
    • typesBySubscriber--<subscriber, eventType>,主要用于取消注册,用于快速查找对象订阅的事件类集合;
    • stickyEvents保存了stickyEvent的最近一次事件实例。
  • 相关阅读:
    二级指针也引用
    可视化 linux 无法启动eclipse 报错No java virtual machine
    java报错java/lang/NoClassDefFoundError: java/lang/Object
    hadoop多次搭建后,完整总结(累死宝宝了,搭建了十多遍了)
    hadoop显示ConnectionrRefused
    hadoop搭建初步总结
    VMware 12 的vmware tools安装和如何使用(系统是CENTOS6.5)
    LInux配置jdk(mac和windows)
    LInux javac时, 提示command not found
    myeclipse如何修改Web项目名称,eclipse如何修改项目名字
  • 原文地址:https://www.cnblogs.com/moderate-fish/p/7680371.html
Copyright © 2011-2022 走看看