前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android EventBus3.0源码分析

Android EventBus3.0源码分析

作者头像
曾大稳
发布2018-09-11 10:39:12
4100
发布2018-09-11 10:39:12
举报

在我们开发过程中,相信应该有很多人使用过EventBus 3.0,这个确实方便了我们,少些了很多代码,这是个优秀的库,我们接下来进行对他剖析。 我们使用EventBus 3.0的过程:

EventBus.getDefault().register()
EventBus.getDefault().post()
EventBus.getDefault().unregister()

我们先看看是怎么初始化的

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

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

一个单例模式,接着点你会发现其实是利用的Builder模式,而框架内部帮我们写了一个EventBusBuilder

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

 EventBusBuilder() {

  }

EventBusBuilder初始化的时候除了对成员变量的一些初始化外,其他的并没有做什么操作。 接下来我们进入register函数

/**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //根据subscriberClass 获取订阅的方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
             //遍历订阅
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

从这个函数我们可以很清晰的看到逻辑,首先根据subscriber(Activity,Fragment)得到相应的订阅方法,然后在遍历订阅。这里代码就不点进去看了,主要说下框架的实现思路。

采用了缓存加反射的方式,主要的参数和类为下: METHOD_CACHE:一个key为订阅者classvalue为需要订阅实现的方法的一个ConcurrentHashMap,这个HashMap是为了保证线程并发安全。我们获取的订阅方法就会缓存到这里面。 FindState:顾名思义,这是一个检测状态的类,里面的参数为

final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
       final Map<Class, Object> anyMethodByEventType = new HashMap<>();
       final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
       final StringBuilder methodKeyBuilder = new StringBuilder(128);

       Class<?> subscriberClass;
       Class<?> clazz;
       boolean skipSuperClasses;
       SubscriberInfo subscriberInfo;

里面比较重要的几个参数: SubscriberMethod: 当前订阅对象(Activity,Fragment)的订阅方法实体。

public class SubscriberMethod {
    //方法
    final Method method;
//模式
    final ThreadMode threadMode;
//类型, 就是参数的类型   参数只能为1个
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;

subscriberMethods:用来存储当前订阅对象(Activity,Fragment)内的订阅方法SubscriberMethodanyMethodByEventType: key为参数类型(订阅类型),value为方法的一个缓存。 subscriberClassByMethodKey:key订阅方法名+">"+参数类型name,value为订阅方法的class

获取订阅方法的时候先判断缓存里面是否存在,不存在就获取FindState实例,根据反射获取注解Subscribe(Activity,Fragment)的方法,然后对FindState实例进行操作(anyMethodByEventTypesubscriberClassByMethodKey得到相应的值或添加到缓存),获取到的订阅方法集合其实就是FindState实例里面的subscriberMethods,最后在把获取的订阅方法集合加入缓存。 得到了订阅方法集合接下来就遍历subscriberMethods

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
 private final Map<Class<?>, Object> stickyEvents;


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

订阅的时候,首先根据订阅者(Activity Fragment)SubscriberMethod得到一个Subscription对象,这个类就是一个真正执行信息传递的订阅对象。

/*
 * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.greenrobot.eventbus;

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

接着把这个Subscription对象添加CopyOnWriteArrayListCopyOnWriteArrayList是一个适合用在读多,写少并发应用中,它是一个线程安全的集合)然后将这个CopyOnWriteArrayList添加到subscriptionsByEventType里面,这个subscriptionsByEventType是一个key为订阅方法的类型(方法函数的类型),value为一个存放SubscriptionCopyOnWriteArrayListHashMap。接下来把订阅方法的类型(参数类型)eventType添加到一个ArrayList里面,在将这个ArrayList添加到typesBySubscribertypesBySubscriber是一个key为订阅者对象(Fragment,Activity),valueArrayList<Class<?>>HashMap,最后在判断是否是sticky,如果是的就将遍历一个stickyEventsHashMap,然后根据key(订阅方法类型)发出消息。

我们看看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 {
           Log.w(TAG, "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--;
               }
           }
       }
   }

很简单,先根据订阅者(Activity Fragment)获取到typesBySubscriber里面的订阅方法类型集合(List<Class<?>> subscribedTypes),然后遍历这个集合,根据这个订阅方法类型得到subscriptionsByEventType里面的订阅对象集合List<Subscription> subscriptions,在遍历这个subscriptions集合,判断subscription.subscriber == subscriber然后移除,最后在移除typesBySubscriber里面的这个订阅对象(Fragment ,Activity )

接下来我们看看post,最终会走到这个函数

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


/**
     * Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents race conditions
     * between {@link #unregister(Object)} and event delivery. Otherwise the event might be delivered after the
     * subscriber unregistered. This is particularly important for main thread delivery and registrations bound to the
     * live cycle of an Activity or Fragment.
     */
    void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        PendingPost.releasePendingPost(pendingPost);
        if (subscription.active) {
            invokeSubscriber(subscription, event);
        }
    }

    void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

在发送消息的时候根据消息类型(参数类型)然后从上面的subscriptionsByEventType里面取出相应的数据进行封装成一个PendingPost对象,在根据反射invoke对应的方法即可,如果是MAIN的话就通过Handler进行线程转换,如果是BACKGROUND并且是在主线程中调用或者是ASYNC将会通过线程池来进行线程切换。

接下来看看postSticky

/**
     * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
     * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
     */
    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);
    }

这个其实我们在注册的时候已经分析了,当调用这个函数的时候,将会将这个消息加到stickyEvents里面,这个stickyEvents是一个ConcurrentHashMap,ConcurrentHashMap是一个应用于高并发的键值对,接着调用post函数先把已经注册的观察者的方法实现,接下来其他对象注册的时候

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

在这里将会调用。 还有一个优先级priority问题,可以看到也是在注册的时候

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

其实也是很简单的原理。源码分析得也差不多了,具体的需要大家自己去查看源码。


总结,EventBus的实现方式:反射 + 数据封装 + 缓存 + 线程切换 通过查看EventBus的源码收获了什么?

1. Builder模式封装参数 2. 根据用处不同进行对不同的类封装,比如:`SubscriberMethodFinder`用于订阅方法的查找;`FindState`一个用`SubscriberMethodFinder`查找的辅助类,里面封装了一些数据;`SubscriberMethod`订阅方法实体,就是通过注解的方法对应的对象;`Subscription`一个订阅对象。 3. 缓存 4. 线程切换使用方式 5. 反射用法熟悉加强 6. `ThreadLocal`是一个关于创建线程局部变量的类。 通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用`ThreadLocal`创建的变量只能被当前线程访问,其他线程则无法访问和修改。 7. `CopyOnWriteArrayList`:是一个适合用在读多,写少的并发应用中,它是一个线程安全的集合 8. `ConcurrentHashMap`:是一个应用于高并发的键值对

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档