EventBus 3.0 使用详解源码分析(三)

目录

前言

Post流程

总结


  • 前言

            前面的文章已经写完了注册流程,现在开始写post流程,如果没有看过的读者,可以通过eventbus注册流程传送。

  • Post流程

           首先EventBus#getDefault()获取实例,而在这里还有一个初始化的方法,通过EventBusBuilder来实现初始化:

 public EventBus build() {
        return new EventBus(this);
    }

         既然EventBus是一个模仿单例的模式设计的,为什么又会存在public的构造函数,这是因为eventbus这样设计,可以同时开始多条事件总线而不会彼此影响。因此开发的时候也要注意,不能一会通过builder构造,一个用getDefault()获取,如果不弄清楚可能会造成订阅者无法处理发布消息的情况。

         接着来看Event#post(Object o)函数的流程:

/** Posts the given event to the event bus. */
    public void post(Object event) {
        //currentPostingThreadState是一个本地线程,一定要了解本地线程的机制,因为本地线程
        // 通过get操作获取不同线程的PostingThreadState
        //PostingThreadState这个内部类描述了发布者线程属性信息,订阅事件对象,以及观察者信息,
        PostingThreadState postingState = currentPostingThreadState.get();
        //获取postingThreadState中保存订阅消息的列表,并将此事件保存。
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        //isposting 默认false
        if (!postingState.isPosting) {
            //判断是不是主线程发布信息
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                //遍历发布队列中的所有的订阅事件
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

    这里要了解PostingThreadState这个内部类。它描述了发布,订阅,事件三者的信息。按照顺序接下来要看BustEvent#postSingleEvent函数:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        //获取发布事件的类型
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        //默认时候true
        if (eventInheritance) {
            //将订阅事件的类型的父类和实现的接口都检索出来。
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            //遍历eventypes将所有事件发布出去,因此注册了该事件父类的观察者也会处理相应的消息。
            //比如MessageObject 父类是MessageA, 那么发布MessageObject消息的时候,也会把MessageA发布出去。
            //因此相关的观察者也会调用。
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                //通过postSingleEventForEventType让订阅者实现订阅事件的处理。
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            //通过builder初始化EventBus设置eventInheritance为false就不会再检索父类,这样可以提高效率。
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

        通过lookupAllEventTypes()获取消息类的父类和实现的接口,这个要看我们的使用需求。是否需要发送消息的时候将父类也一同post然后可以通过配置来确认。按顺序执行EventBus#postSignleEventForEventType()

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //前文提到过subscriptionsByEventType保存了订阅事件和订阅者的信息,在这里根据事件类型获取所有订阅者信息。
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                //遍历所有的观察者,通过postToSubscription函数来实现观察者方法的反射执行
                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;
    }

       在这里就是根据订阅事件类在SubscriptionByEventType这个map里面检索出所有的观察者,因为在前文中遍历了订阅事件的父类,所以这里会多次调用。将订阅事件的父类的所有观察者也会检索出来依次分发事件。之后在EventBus#postToSubscription里面执行最终的逻辑反射调用。


    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //在这里通过不同的线程属性来实现不同的调用方法。最终都是通过invokeSubscriber()来实现
        //invokeSubscriber()函数就是单纯的反射来实现。
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(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);
        }
    }

         在这里根据注册者中注册方法注解里面的线程模式来进行不同的分发。首先如果是Posting模式,那么直接通过反射执行事件处理,典型的从哪里来到哪里去,这也是默认的方式。然后是Mian模式,如果事件是ui线程发出的,那么直接在ui线程处理。如果是线程发出的,那么通过mainposter会把时间发送到ui线程。如果是Background模式,如果事件是在ui线程发出的,那么会重新开一个线程执行,否则就直接执行,还是从哪里来,到哪里去。如果是async模式,直接开一个线程就ok。这里需要说的是mainPoster,backgroundPoster和asyncposter三个分发处理。如果是主线程发出的在MAIN这种模式下。直接通过调用invokeSubScriber()直接通过反射就执行了。遵从从哪里来到哪里去的原则。如果是从线程里面发出的事件,需要在ui线程处理。那么就需要将事件post到ui线程,可以想想应该用到了handler。

public class HandlerPoster extends Handler implements Poster {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

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

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

       MainPoster就是这个HandlerPoster,首先继承hander,关于handler无需多讲,再看实现了接口Poster,各个类型的poster都实现了这个接口,这个接口也只有一个方法enqueue(),它主要实现了将post事件信息添加到链表中。而mainPoster也主要是将各个事件通过handler发布出去。然后也是通过invokeSubscriber来实现。当处理一个事件的时候,其他事件等候,顺序执行。这里面涉及到PendingPostQueue, 他是一个类似队列的数据机构模式,提供入队和出队的操作,没有什么复杂的地方,大家可以自己看看。需要说的是队列中的元素类型PendingPost.

final class PendingPost {
    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

    Object event;
    Subscription subscription;
    PendingPost next;

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

    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实现了一个对象池的概念,final类,然后构造函数是私有的。 从而在线程上也是安全的。在这里只能获取PendingPost对象,之后不用的时候释放。 从而不会使内存爆发式增长。 其实eventbus还有其他地方也涉及到对象池的概念。 大家自己分析的时候会看到。就不一一举例了。之后我们看BackGroundPoster

final class BackgroundPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

       BackgroundPoster这里实现了Runnable,通过这样来实现在新的线程处理事件, 其中通过if(!executorRunning)可以看出添加的是时候当一个事件执行的时候,其他事件只是会被添加到队列中去,但是不会被立刻执行,所以在run中有一个while循环来不停的获取队列中的事件去执行。因此可以看出,Background模式不宜执行太耗时的操作。接下来看AsyncPoster,

class AsyncPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

}

     可以看出,Asyncposter也实现了Runnable,但是他就是直接new一个线程就不管了。所以可以在这个模式下执行耗时操作。到这里post的流程基本结束了。大致可见post流程也可以分为三个流程:

           1 通过事件将观察者检索出来。

           2 根据ThreadMode模式将分发事件的方式确定。

           3 通过反射处理事件。

  • 总结

          大致跟随源码讲了一下eventbus的流程,其实还有很多需要注意的地方,比如黏性和优先权。 之后会在剩下的博客里说一说。

猜你喜欢

转载自blog.csdn.net/alvinhuai/article/details/81512253