美文网首页
EventBus源码分析

EventBus源码分析

作者: minminaya | 来源:发表于2018-02-18 16:50 被阅读733次

好久没写博文了,这几个月复习了很多东西,新年在老家闲的慌啊,我这么爱学习的人只能学习了哈啊哈哈哈哈哈哈(滑稽脸)。顺便总结总结一些容易忘的东西。

EventBus的使用相信大家都使的贼溜了。实在不行看文档嘛。本文只要是分析下EventBus的源码执行过程,分析分析设计的思路,这样对使用和深入学习甚至改进轮子都有令人窒息的好处。


这张是EventBus官网档下来的哈哈哈哈哈!

很明显啦,EventBus用的观察者模式,Publisher作为事件发布者(被观察者)把Event发布到中转服务站EventBus,EventBus把Event传给Subscriber事件订阅者(观察者)并且唤醒了它。


image

一. EventBus结构

根据EventBus源码,其架构可以分为5个部分

  1. 构造方法
  2. 订阅者注册
  3. 发送事件
  4. 根据不同线程参数等处理事件(用反射运行目标方法)
  5. 订阅者取消注册

1. 构造方法

EventBus注册的入口是EventBus.getDefault().register(this)

getDefault()方法如下


 static volatile EventBus defaultInstance;

 public static EventBus getDefault() {
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    instance = EventBus.defaultInstance = new EventBus();
                }
            }
        }
        return instance;
    }

这里用了双重检查模式的单例模式。并且这里很高明的使用volatile 避免的指令重排序引起 未被完全初始化的问题。
什么?不懂重排序?
没事,重排序其实通俗的讲就是虚拟机为了优化指令,提高程序运行效率搞的东西。或者这么说,本来1,2,3三条指令按顺序运行,重排序之后那可能会变成1,3,2。这样会影响某些对象的初始化问题。

虽然效率双重检查模式会有一定影响运行效率,但是对同步代码天大的好处。

回头来,EventBus()的构造方法


private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
        this(DEFAULT_BUILDER);
    }

这货,搞了个独生子EventBusBuilder(),让它全权代理各参数以及配置的存储(其实也就是设计模式中的Builder模式)

EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        ...省略一大堆妃子.....
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

2. 订阅者注册

接着到了register()方法

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

这里有俩个地方很关键,一个是1. findSubscriberMethods()方法还有一个是2. subscribe()方法,他们一个是为了找到订阅方法,一个是用来注册订阅方法。

1方法是为了找到订阅者传进来的所有订阅方法,当然里面还挺复杂的,还写了判断有没有缓存来提高效率。

findSubscriberMethods()中用了findUsingInfo()方法来获取订阅者的各种信息,并且进而调用了findUsingReflectionInSingleClass()来反射获取订阅者中的方法还有将他们保存到findState里面。

总结起来1方法也就是找到所有订阅方法,保存订阅方法信息到findState

2方法是为了注册所有的订阅方法,当然还有粘性事件的处理,解析可以看下注释

 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        //根据订阅者和订阅对象创建订阅对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //根据事件类型获取订阅对象集合
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(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;
            }
        }

       .....
        
        if (subscriberMethod.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();
                         //从上面stickyEvents集合中获取该类型的事件发送给当前订阅者。下面else里头也一样
                         checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

这里有点特别的地方就是,粘性事件的处理,如果是粘性事件,那么从粘性事件的集合中取出该事件发送到当前订阅者。


3. 发送事件

这里从currentPostingThreadState对象中取出事件队列并且将当前需要的事件保存到队列中,然后处理队列中的所有事件,哦,同时移除操作的事件

 public void post(Object event) {
        //事件队列和线程状态信息的Model类
        PostingThreadState postingState = currentPostingThreadState.get();
        //获取事件队列
        List<Object> eventQueue = postingState.eventQueue;
        //将事件插入到队列中
        eventQueue.add(event);

        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()处理事件,并且移除该事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

又回到postSingleEvent()方法里,从这里开始向下,又是一系列的对方法的查找的算法(向上查找事件的父类),结果是查找到所有的父类事件后存在List中,然后通过postSingleEventForEventType(event, postingState, eventClass)方法对各个事件分别处理,这里的函数名可以看出。。。。。
这里处理的关键在于postToSubscription(subscription, event, postingState.isMainThread);

另外,假如是粘性事件,那么,
从入口函数开始

EventBus.getDefault().postSticky(object);

它会把粘性事件的object放到下面这个Map里面

private final Map<Class<?>, Object> stickyEvents;

便于第2步的subscribe()方法处理粘性事件

4. 根据不同线程参数等处理事件(用反射运行目标方法)

从第3步的最后那里,可以知道事件给了postToSubscription(subscription, event, postingState.isMainThread);处理,这个方法很有意思,

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

还记得使用EventBus定义处理事件的注解吗,也就是EventBus的4种线程模式,再看看上面的switch语句的处理,是不是遇到了父老乡亲,此处应该两眼泪汪汪。。

这里根据不同的线程模式,来处理各个方法(反射执行方法),比如ThreadMode是MAIN,那么这里假如不是主线程的方法,则用Handler切换到主线程再执行。


5. 订阅者取消注册

取消注册那就是跟注册是相反的,大体原理一样,不过是变成了遍历取消注册


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 {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

这里最关键的地方在遍历函数里头的

unsubscribeByEventType(subscriber, eventType);

它是为了从订阅对象集合里面逐个逐个移除subscriber(订阅者)


总结

我画了张思维导图,可能要放大才清楚咯

EventBus.png

相关文章

网友评论

      本文标题:EventBus源码分析

      本文链接:https://www.haomeiwen.com/subject/uaqbtftx.html