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

来源:互联网 发布:京都 知乎 编辑:程序博客网 时间:2024/05/29 02:50

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

动机

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

什么是EventBus

总结为以下几点:

  • EventBus是一个发布 / 订阅(Subscriber/Publisher)的事件总线,内部是靠Handler发送Message来进行通信的。
  • EventBus不是基于注解的,基于命名规定的,即以“onEvent”开头的。
  • 事件驱动
  • EventBus可以在多线程下订阅消息。
    这里写图片描述
    上面这几点,下面都会一一解释的。
    先看一段EventBus的简单使用代码:
//SubscriberActivitypublic 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个类之间的关系:
这里写图片描述

对于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 => 该类中所有的订阅方法//因为反射比较耗时间,所以这里声明为staticprivate 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);            }        }    }

这里写图片描述
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。这也就是门面模式的使用。
Facade

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

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


邮箱:1906362072@qq.com

1 0
原创粉丝点击