EventBus原理+源码解析(图文并茂)

软件工程大二学生一枚,对Android的理解不是很透彻,若有失误希望大家指正,谢谢大家。

动机

在使用EventBus的时候,觉得比较好用,所以就准备去看源码。本文主要介绍了EventBus怎么实现的(从设计思想到每一行代码)。

什么是EventBus

总结为以下几点:

  • EventBus是一个发布 / 订阅(Subscriber/Publisher)的事件总线,内部是靠Handler发送Message来进行通信的。
  • EventBus不是基于注解的,基于命名规定的,即以“onEvent”开头的。
  • 事件驱动
  • EventBus可以在多线程下订阅消息。

    上面这几点,下面都会一一解释的。
    先看一段EventBus的简单使用代码:
//SubscriberActivity
public class SubscriberActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_subscriber);
        EventBus.getDefault().register(this);
    }

    public void onEvent(Event aEvent)
    {
        //do something with aEvent
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}
//PublisherActivity
//可以在任意地方调用
EventBus.getDefault().post(new Event());

构造EventBus

EventBus源码文件夹里面,首先可以看到2个类,EventBusEventbusBuilder
EventBus定义了所有的入口方法;
EventbusBuilder利用Builder模式来构造EventBus

上述例子中,调用了下面的方法:

    static volatile EventBus defaultInstance;
    /** * 双重同步锁,利用单例模式(Singleton)构造一个EventBus对象 */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

进入EventBus的构造函数:

    public EventBus() {
        this(DEFAULT_BUILDER);
    }
    /** * 一大推参数暂时不解释,等哈在讲 * @param 为一个EventBusBuilder对象 */
    EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
        typesBySubscriber = new HashMap<Object, List<Class<?>>>();
        stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

EventBusBuilder类里面:

   /** * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be * done only once before the first usage of the default EventBus. * * @throws EventBusException if there's already a default EventBus instance in place */
    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

    /** Builds an EventBus based on the current configuration. */
    public EventBus build() {
        return new EventBus(this);
    }

典型的构造者模式,不必多说,可以参见NotificationNotificationBuilder来学习。
我们可以通过以下方式来创建EventBus对象:

  • EventBus.getDefault();
  • 通过EventBusBuilder自行定制参数,再来创建。

Poster

上文说到:EventBus是一个发布 / 订阅(Subscriber/Publisher)的事件总线。那么Subscriber Publisher之间怎么联系,就要用到了Poster
Poster有以下几种:

  • HandlerPoster
  • BackgroundPoster
  • AsyncPoster

重点介绍HandlerPoster,其他2个差不多。

Step1:先看看PendingPost

final class PendingPost {
    ////单例池,复用对象
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

    Object event; //事件类型(就是onEvent(Event aEvent)里面这个参数的类型)
    Subscription subscription; //订阅者的封装类
    PendingPost next; //指向队列下个元素的指针

    private PendingPost(Object event, Subscription subscription) {
        this.event = event;
        this.subscription = subscription;
    }

    //首先检查复用池中是否有可用,如果有则返回复用,否则返回一个新的
    //因为在池中数据都是null 所以只要池中有数据就拿出来用
    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        //这里加锁,想想为什么?
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

    //回收一个待发送对象,并加入复用池
    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            // 池的最大限制
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }

}

有以下几点说明:

  • PendingPost内部维护了一个池,暴露的方法只有2个,obtainPendingPost releasePendingPost
  • 对于eventsubscription,等哈再说。看作节点中的数据块就好了。

Step2:PendingPostQueue

final class PendingPostQueue {
    private PendingPost head;
    private PendingPost tail;

    synchronized void enqueue(PendingPost pendingPost) {
        if (pendingPost == null) {
            throw new NullPointerException("null cannot be enqueued");
        }
        if (tail != null) {
            tail.next = pendingPost;
            tail = pendingPost;
        } else if (head == null) {  //第一次的时候调用
            head = tail = pendingPost;
        } else {
            throw new IllegalStateException("Head present, but no tail");
        }
        notifyAll();// 因为入队之后,队列中存在数据,唤醒下面的线程,即出队操作继续执行
    }

    synchronized PendingPost poll() {
        PendingPost pendingPost = head;
        if (head != null) {
            head = head.next;
            if (head == null) {
                tail = null;
            }
        }
        return pendingPost;
    }

    synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
        //当head == null的时候,该方法处于阻塞的状态。
        //直到maxMillisToWait时间到了 或者 enqueue调用notifyAll()
        if (head == null) {
            wait(maxMillisToWait);
        }
        return poll();
    }

}

队列没什么好说的,自己去看数据结构书去。然后wait() notify()可以参考这些[Java 并发]多线程怎么进行同步(二)。

Step3:HandlerPoster

final class HandlerPoster extends Handler {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive; //判断当前queue中是否有正在发送对象的任务

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

    //入队方法会根据参数创建 待发送对象 pendingPost 并加入队列,如果此时 handleMessage() 没有在运行中,则发送一条空消息让 handleMessage 响应
    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) { //当前消息队列中 没有PendingPost了
                            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;
        }
    }
}

注意几点:

  • Handler的知识忘了,可以看这里。
  • handlerActive这个成员,是判断该Handler里面的队列是否有消息正在处理。
  • eventBus.invokeSubscriber(pendingPost);这段代码才是真正的调用Subscriber的方法。等哈在说。
  • if (timeInMethod >= maxMillisInsideHandleMessage)是为了防止,主线程阻塞,如果主线程出现了 ANR,就退出当前循环,然后再sendMessage(obtainMessage()

来张图梳理一哈,上面3个类之间的关系:
EventBus原理+源码解析(图文并茂)_第1张图片

对于AsyncPoster BackgroundPoster,里面调用下面这个函数:

eventBus.getExecutorService().execute(this);

ExecutorService是Java1.5出来一个用来管理线程的一个工具类,通过操作java.util.concurrent里面的包而不是Thread本身,可以更好的操作线程。

Subscriber

Step1:概念解释:

  • Subscriber:可以把他理解为Register,就是在本文一开始的代码中,调用EventBus.getDefault().register()的类。
  • Event:就是一个事件, Subscriber要对这个时间进行处理。

Step2:EventBus中有几个Map是什么:

   /** * key => event的Class类型(Event.class) * value => 可以响应该event的所有订阅者(onEvent*()函数的参数为event类型) */
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    /** * key => 订阅者 * value => 订阅者的所有的订阅方法 */
    private final Map<Object, List<Class<?>>> typesBySubscriber;
     /** * 粘性事件 */
    private final Map<Class<?>, Object> stickyEvents;

注意:

  • 上面的理解不了暂时没事,下面会一一解说
  • stickyEvents这里先不说,等哈在说

Step3:register()函数

         private synchronized void register(Object subscriber, boolean sticky, int priority) {
        //使用subscriberMethodFinder来查找subscriber中所有的SubscriberMethod(订阅方法,onEvent()等方法)
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        //循环每一个事件响应方法 将订阅者存储起来
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

里面出现了几个类:
SubscriberMethod: 对订阅方法(在订阅者里面以onEvent开头的函数)的封装类。(后文直接称为订阅方法)

final class SubscriberMethod {
    final Method method; //方法
    final ThreadMode threadMode; //一个枚举类型,包括EventBus四种通知模式。
    final Class<?> eventType; //所属的类的类类型
    /** Used for efficient comparison */
    String methodString;

    //将上面参数进行处理
    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            StringBuilder builder = new StringBuilder(64);
            builder.append(method.getDeclaringClass().getName());
            builder.append('#').append(method.getName());
            builder.append('(').append(eventType.getName());
            methodString = builder.toString();
        }
    }
}

subscriberMethodFinder: 利用反射,找到一个类里面所有的SubscriberMethod。基础知识,自己去看看反射的用法。

不过有2点要注意:

 // 反射过程中,会搜索 ON_EVENT_METHOD_NAME 开头的函数,这就是为什么本文一开始提出的 
 // EventBus不是基于注解的,基于命名规定的,即以“onEvent”开头的,在这里可以修改。
 private static final String ON_EVENT_METHOD_NAME = "onEvent";
//key => 类名
//value => 该类中所有的订阅方法
//因为反射比较耗时间,所以这里声明为static
private static final Map<Class<?>, List<SubscriberMethod>> methodCache = new HashMap<Class<?>, List<SubscriberMethod>>();

这样register()方法就算可以了:利用反射找到Subscriber内的所有的SubscriberMethod,然后遍历,调用subscribe()

Step4:subscribe()订阅方法的真正执行者,将订阅方法存储起来

这里用到了上面介绍的Map,可以在这里加深印象(这段代码比较男懂)

// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {

        // 构建 subscriptionsByEventType的映射
        Class<?> eventType = subscriberMethod.eventType; //event的Class类型
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
        if (subscriptions == null) { //第一次时调用
            subscriptions = new CopyOnWriteArrayList<Subscription>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
            }
        }

        // 更具优先级来构建 subscriptions
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        // 构造 typesBySubscriber
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) { //第一次时调用
            subscribedEvents = new ArrayList<Class<?>>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        //粘性事件,暂时不说
        if (sticky) {
            if (eventInheritance) {
                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);
            }
        }
    }

EventBus原理+源码解析(图文并茂)_第2张图片
OK,Subscriber总算是完成了。

Poster

Step1:Poster就是调用EventBus.getDefault().post(new Event())所属的类
Step2:post()

/** Posts the given event to the event bus. */
    public void post(Object event) {
        //得到当前线程下的 PostingThreadState
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        //当前的 postingState不再 postSingleEvent执行之下
        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 {
                //主要代码,当队列不为空时,将队列元素取出,执行postSingleEvent
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

上面代码出现的几个类:
PostingThreadState: EventBus静态内部类

final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<Object>(); //队列
        boolean isPosting; //是否执行postSingleEvent
        boolean isMainThread; //是否在主线程
        Subscription subscription; //订阅者
        Object event; //事件
        boolean canceled;
    }

currentPostingThreadState:一个ThreadLocal<PostingThreadState>对象,ThreadLocal存储当前线程下的内容,自己Google。

 private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

最后,队列中的每个元素,都会执行postSingleEvent

Step3:postSingleEvent()

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass(); //得到event的所属类的类类型
        boolean subscriptionFound = false;
        if (eventInheritance) { // 表示一个子类事件能否响应父类的 onEvent() 方法。
            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));
            }
        }
    }

说明一哈:
eventInheritance设置响应可以继承,也就是一个子类事件能否响应父类的 onEvent() 方法,然后lookupAllEventTypes利用递推,遍历得到所有的父类的onEvent()

Step4:postSingleEventForEventType()

       /** * @param event 需要Post的事件 * @param postingState * @param eventClass event的Class类型 */
    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            // 得到 能够响应 eventClass的所有订阅者
            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;
    }

注意一哈:

  • subscriptions = subscriptionsByEventType.get(eventClass);还记得 subscriptionsByEventType这个Map是干什么的吗?这句话的代码就是得到 可以响应eventClass的所有订阅者。

Step5:postToSubscription

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case PostThread:
                invokeSubscriber(subscription, event);
                break;
            case MainThread:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BackgroundThread:
                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);
        }
    }

这里才真正的把上文所有的内容贯通起来。首先,ThreadMode是一个枚举类型。我们一个个来看:

  • PostThread:订阅函数响应的线程和Poster在一个线程。
  • BackgroundThread:当Poster在主线程时,订阅事件就在新开线程,当Poster不在主线程时,订阅函数就在当前线程执行。
  • Async:始终新开一个线程执行订阅事件,所以在这里可以执行耗时任务。
  • MainThread:始终在主线程执行,用于更新UI。
    这也印证了一开始说的 EventBus可以在多线程下订阅消息。

mainThreadPoster.enqueue(subscription, event);
backgroundPoster.enqueue(subscription, event);
asyncPoster.enqueue(subscription, event);
就是调用前文所说的Poster#handlerMessage()

最后总结一哈 EventBus的原理:

粘性事件

何为粘性事件,我是从粘性广播(StickyBroadcast)里面悟出来的。可以看这里

我的理解如下:
1. 首先调用post()事件,EventBus将事件存储起来。
2. 在调用register(),EventBus把存储起来的post()在给订阅函数。
因为post()没有消失,才称为粘性事件吧。

这样EventBus给我们准备了几个特别的函数registerSticky() postStivky()
同时使用stickyEvents来存储事件。

   /** * 粘性事件 */
private final Map<Class<?>, Object> stickyEvents;

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

最后,unregister()类似,大家可以再次阅读源码理解更多。有什么问题咱们一起讨论。

一些思考

EventBus采用了Facade模式,即提供一个统一的接口给用户,用户只要写onEvent*()方法,就可以被EventBus识别,内部隐藏这些判断,用了EventBus这个门面把具体的操作分发给了不同的SubSystem,也就是HandlerPosterMainThreadPoster这些Poster。这也就是门面模式的使用。
EventBus原理+源码解析(图文并茂)_第3张图片

EventBus采用事件驱动的思想。那么什么是事件驱动呢?说白了,就是一个事件A的调用时由于另一个事件B的触发而被调用,所以说是事件A是由事件B来驱动的。那么什么地方运用的最多呢?前段啊。就好说,一个按钮绑定一个事件,当用户点击这个按钮的时候(这是一个事件)这个按钮所绑定的事件就会被调用。
所以在EventBus里面可以看作事件驱动的。当onEvent()绑定了一个事件后,并没有直接被调用,而是由post驱动的。而事件驱动器也就是EventBus.java这个类,底层是Handler。事件驱动将每个类的调用关系变得简单,没有那么多的回调函数。

原来在想事件驱动和异步是一个东西吗?当然不是啦。异步是我发消息之后(发送消息可以当作成一个事件),我就去做其他事,等你事做好了再来通知我。事件驱动是我做出了发送消息的事件会驱动你去做你要做的事。2者还是有微妙的区别的。
可以参考下面这些连接:
http://www.infoq.com/cn/articles/what-is-nodejs
http://www.infoq.com/cn/news/2011/09/nodejs-async-code

但是EventBus也是有缺点的:
- 事件的订阅者又去充当发布者的话,会使得整个代码逻辑变得混乱。
- 一个订阅者可以接收到许多发送者的消息,但是有些消息是不想被订阅的。

事件驱动机制跟消息驱动机制相比

邮箱:[email protected]

你可能感兴趣的:(android,EventBus)