EventBus 源码分析
来源:互联网 发布:淘宝怎么看在线人数 编辑:程序博客网 时间:2024/05/22 01:53
EventBus 源码分析
先上github 地址:eventbus
首先,说说Eventbus 是什么,能干什么
其为android中的事件总线框架,用于在app内不同模块间通信消息传递的框架。简单的说,就是一个地方发送一个广播,在注册了该广播的地方接受到广播(在对应的函数中)。
怎么使用?
1.需要创建一个类,该类对应着一类事件。(相当于广播中的,意图过滤器中的条件)
class MyEvent{} // 代表一类事件。 即一类的广播。
2.在需要相应的类中,进行注册。(对应的要在该类销毁的时候,进行注销操作)
EventBus.getDefault().register(this);// 注册该类为接受者EventBus.getDefault().unregister(this);// 注销该类。
3.在注册的类中,添加对应接受事件的函数。比如
public void onEvent(MyEvent event) {// TODO 在接收到该事件时,我们要进行的相应处理。}
4.发送一个事件。也可以理解为发送一个广播。
EventBus.getDefault().post(new MyEvent());// 发送的MyEvent对象,会被注册了该事件的方法中得到回调。
简单的用法就是这些了。
eventbus 源码
我们就从其暴露给我们的接口来一步步看起源码。
首先看从getDefault方法开始。
// 这里可以看到一个典型的创建的单例public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
在eventbus的构造函数中
EventBus(EventBusBuilder builder) { subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();// key 为所以Event的class,value为 该注册了该event的所以类。 typesBySubscriber = new HashMap<Object, List<Class<?>>>(); // key为注册event的类,value为该类中的所有event的class。 以上两 个集合非常重要,会频繁出现在下面的分析中。 stickyEvents = new ConcurrentHashMap<Class<?>, Object>(); // 保存粘性事件的集合。 key为event.class value为该class对应的对象实例。 // 以上三个map用来存放我们注册的事件相关的数据。 mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); backgroundPoster = new BackgroundPoster(this); asyncPoster = new AsyncPoster(this); // 以上三个类,用来实现在不同线程中回调onEvent()函数。第一个为在通过handler实现主线程的回调。 后两个都是runnable的子类,是实现在子 线程中回调。 subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);// 该类为一个功能性辅助类,在该 类中主要实现在一个类中,查找出所以得 onEvent方法。 // 以下为以下简单的配置信息设置。 logSubscriberExceptions = builder.logSubscriberExceptions; logNoSubscriberMessages = builder.logNoSubscriberMessages; sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; sendNoSubscriberEvent = builder.sendNoSubscriberEvent; throwSubscriberException = builder.throwSubscriberException; eventInheritance = builder.eventInheritance; executorService = builder.executorService; }
然后我们在看一下register(Object subscriber)方法,都会调用一个方法。
//subscriber 为注册的类,sticky 是否是粘性事件,priority为优先级 ,会影响存入map的顺序,进而影响回调的顺序。private synchronized void register(Object subscriber, boolean sticky, int priority) { List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod, sticky, priority); } }
首先,我们看到首先调用了SubscriberMethodFinder的findSubscriberMethods方法。给类做的事情就是 把 订阅了事件的类中的所有的onEvent回调函数给返回出去。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { String key = subscriberClass.getName(); List<SubscriberMethod> subscriberMethods; synchronized (methodCache) { subscriberMethods = methodCache.get(key);// methodCache 为一个static变量,注册的类为key,以该类中所有注册函数的list为value,相当于一级缓存。将所有的注册类中的注册方法保存下来。避免多次遍历类中方法的操作。 } if (subscriberMethods != null) { return subscriberMethods; // 如果已经处理过该类,直接使用缓存下来的数据返回。 } subscriberMethods = new ArrayList<SubscriberMethod>(); Class<?> clazz = subscriberClass; HashSet<String> eventTypesFound = new HashSet<String>(); // 保存已经添加的方法和该方法的参数拼接的字符串,用于防止重复添加。 StringBuilder methodKeyBuilder = new StringBuilder(); while (clazz != null) { // 循环遍历该类,以及其父类 String name = clazz.getName(); //忽略掉系统中的类 if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) { // Skip system classes, this just degrades performance break; } // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { // 以OnEvent开始的方法 int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //访问修饰符判断 public且非Ignone方法 Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) {// 只有一个参数的方法 String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length()); ThreadMode threadMode; if (modifierString.length() == 0) { threadMode = ThreadMode.PostThread; } else if (modifierString.equals("MainThread")) { threadMode = ThreadMode.MainThread; } else if (modifierString.equals("BackgroundThread")) { threadMode = ThreadMode.BackgroundThread; } else if (modifierString.equals("Async")) { threadMode = ThreadMode.Async; } else { if (skipMethodVerificationForClasses.containsKey(clazz)) { continue; } else { throw new EventBusException("Illegal onEvent method, check for typos: " + method); } } Class<?> eventType = parameterTypes[0]; methodKeyBuilder.setLength(0); methodKeyBuilder.append(methodName); methodKeyBuilder.append('>').append(eventType.getName()); String methodKey = methodKeyBuilder.toString(); if (eventTypesFound.add(methodKey)) { // Only add if not already found in a sub class SubscriberMethod类为封装了一个类中接收函数信息的封装类。其中包含了方法method、该方法需要回调的线程threadmode、对应的事件类型eventtype。 subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType)); } } } else if (!skipMethodVerificationForClasses.containsKey(clazz)) { Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "." + methodName); } } } clazz = clazz.getSuperclass(); // 获取父类的class,继续循环 } if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " + ON_EVENT_METHOD_NAME); } else { synchronized (methodCache) { methodCache.put(key, subscriberMethods);//将该类中的所有对应的响应方法的集合缓存下来。 } return subscriberMethods; } }
然后,遍历得到集合,调用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)方法。该方法 主要功能就是为了将我们在EventBus 构造函数中创建的subscriptionsByEventType、typesBySubscriber和stickyEvents这三个集合,将对应的数据保存在他们中。前面已经说过他们对应保存的数据,这里就不重复了。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { Class<?> eventType = subscriberMethod.eventType; // 事件的对应的class CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // 先从集合中获取以前保存的。 Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);// subscription 类为对接受事件的类的包装类,里面包含 subscribe r 就是我们register(Object o)时传入的this。一般就是activity或fragment。subscribermethod 就是该类中的onevent方法的封装,priority if (subscriptions == null) {//如果为空,代表第一次处理该event。创建list,并且将list添加到subscriptionsByEventType中。 subscriptions = new CopyOnWriteArrayList<Subscription>(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) //将newSubscription按照优先级添加到中。 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; } }// 到此,subscriptionsByEventType这个集合填充完毕。 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//同样的,先将这个类中之前event.class的集合取出。 if (subscribedEvents == null) { // 如果为空,代表是第一次,创建list集合,并且将其放到typesBySubscriber这个map中 subscribedEvents = new ArrayList<Class<?>>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType);// 将event加到刚才创建的list中。 if (sticky) { // 是否是粘性事件。如果不是的话,register方法到此就结束了。如果是注册的是粘性事件监听,就会在注册的时候,执行以下代码。 Object stickyEvent; synchronized (stickyEvents) { stickyEvent = stickyEvents.get(eventType); // 该集合中的stickyEvent是在postSticky(Object o)的时候,添加到集合中的。所以所谓的粘性事件就是在这类 注册粘性事件监听之前,就已经被post过的事件,在注册的时候会被直接调用,并将之前的Object作为参数传入。 } if (stickyEvent != null) { // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) // --> Strange corner case, which we don't take care of here. postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); // 这里是post粘性事件。该方法是post事件的核心方 法。之后在分析post模块的时候,会继续分析。 } } }
到这里,我们就算走完了全部register的流程。我们现在回顾一下注册所做的事情。其实,最主要的事情就是将注册的类(比如activity)中的对应的onevent回调回调函数(比如 OnEvent(MyEvent myevent)),以及其参数,通过自己定义的包装类,保存到相应的集合之中(subscriptionsByEventType,typesBySubscriber)。
然后,我们再看一看,我们是如何post一个事件是如何实现的。
public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get();//获得在和该线程有关的postingState对象。 List<Object> eventQueue = postingState.eventQueue; // 将event添加到postingState中的list中。 eventQueue.add(event); if (!postingState.isPosting) { //根据postingState的状态,设置一下相应的状态。 postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { while (!eventQueue.isEmpty()) { // 将eventQueue中所有的对象post出去。 postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }
首先,我们先看一下PostingThreadState这个类。它是EventBus的内部类。其中主要是保存事件的一些信息,就是一个bean对象。这里需要特别关注一下currentPostingThreadState这个对象。它是ThreadLocal的实例。ThreadLocal可以理解一个用于在不同线程中保存数据的一个容器。比如你在主线程中添加了一条数据,又在子线程中添加了一条数据,然后,当你在主线程中去get的时候,拿到的就是你之前在主线程中保存的数据,如果你在子线程中去get,那么拿到的就是你在子线程中保存的数据。对于具体如何实现的,有兴趣的同学可以自己去研究研究,其中有用到很巧妙的算法。
然后我们在看看postSingleEvent(Object event, PostingThreadState postingState)方法.
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); // event对应的class boolean subscriptionFound = false; if (eventInheritance) { // 代表是否将event的父类,也进行处理。 List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); // 在该方法中,通过循环,将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); } }
然后,其都会调用postSingleEventForEventType方法,返回的boolean代表是否存在对应的注册者类。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { // 这里我们看到,我们通过eventClass从subscriptionsByEventType中拿到我们之间在注册的时候添加进去的该event的所对应的注册者的集合。 subscriptions = subscriptionsByEventType.get(eventClass); } if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) {// 遍历该event 所有的注册者 postingState.event = event; postingState.subscription = subscription; boolean aborted = false; // 将信息保存到postingState对象中 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; }
在这里然后调用 postToSubscription方法。该方法在之前就有遇到过。实在register时,对于粘性事件我们调用了该方法。现在我们就来看看到底该方法干了什么。
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); } }
我们看到了,主要是通过区分subscription.subscriberMethod.threadMode的类型,以及当前方法执行的线程(也就是post方法调用的时候的线程),来调用不同方法。
这里主要有四种方式。 一种是invokeSubscriber(subscription, event) (其实最后调用的都是这个方法,不过是在不同线程中调用而已)代表在当前线程中调用。第二种 mainThreadPoster.enqueue(subscription, event),在主线程中调用。第三种 backgroundPoster.enqueue(subscription, event) 会在子线程中调用,而且该线程是维护了一个队列,依次处理所有的加入的数据。第四种是asyncPoster.enqueue(subscription, event)会开辟新的子线程在其中执行。
正如我刚才说的,其实最后调用的都是invokeSubscriber(Subscription subscription, Object event)这个方法。 只是通过在不同线程中调用,来实现在不同线程中回调。
void invokeSubscriber(Subscription subscription, Object event) { subscription.subscriberMethod.method.invoke(subscription.subscriber, event); // 实现功能的就是该方法,通过反射调用该方法。 ....... }
现在我们来看看如何实现在不同线程中调用该方法。如果还记得的话,我们在eventbus的构造函数中,曾经创建过三个对象
mainThreadPoster,backgroundPoster,asyncPoster这三个对象,对应于不同线程。
首先,mainThreadPoster其实就是一个handler,而且是主线程中的handler,所以其对应的handleMessage方法会在主线程中执行。
首先调用了enqueue(Subscription subscription, Object event)方法。
首先,我们看PendingPost类,该也是一个封装event和subscription的类,但是不同的是其中包含一个pendingPostPool的存储集合,该集合里面添加进去的是我们之前创建过的PendingPost的对象。其作用是复用以前创建过的对象,减少对象创建的开销。
static void releasePendingPost(PendingPost pendingPost) { pendingPost.event = null; pendingPost.subscription = null; pendingPost.next = null; // 将使用完毕的PendingPost中的数据清空 synchronized (pendingPostPool) { // Don't let the pool grow indefinitely if (pendingPostPool.size() < 10000) { pendingPostPool.add(pendingPost); 添加到集合中 } } }
在我们需要一个新的PendingPost的对象的时候
static PendingPost obtainPendingPost(Subscription subscription, Object event) { synchronized (pendingPostPool) { int size = pendingPostPool.size(); if (size > 0) { //如果集合中有不用的PendingPost,将其取出,并且将对应的数据添加到其中。 PendingPost pendingPost = pendingPostPool.remove(size - 1); pendingPost.event = event; pendingPost.subscription = subscription; pendingPost.next = null; return pendingPost; } } return new PendingPost(event, subscription); }
然后,回到enqueue方法中,
void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); // 获得封装了subscription和event的pendingpost对象 synchronized (this) { queue.enqueue(pendingPost); // 将其添加到自己定义的队列中 if (!handlerActive) { handlerActive = true; if (!sendMessage(obtainMessage())) { // 调用sendmessage方法发送一个空消息,会调用handler的handlemessage方法中。 throw new EventBusException("Could not send handler message"); } } } }
然后,来到handlemessage方法
public void handleMessage(Message msg) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); //获取系统时间,相当于进入循环是的时间,因为我们看到下面是一个死循环,会用这个时间做一个限制。 while (true) { PendingPost pendingPost = queue.poll(); //从队列中取出在enqueue中添加的数据。 if (pendingPost == null) { synchronized (this) { // Check again, this time in synchronized pendingPost = queue.poll(); if (pendingPost == null) { handlerActive = false; return; } } } // 以上部分是为了判断队列是否为空的考虑线程安全的方法。 eventBus.invokeSubscriber(pendingPost); // 这里,就是实现真正功能的代码。我们看到,它又回到了EventBus类中,只不过,它是在主线程中调用的。因为这是在主线程的handler的handlemessage方法中。 long timeInMethod = SystemClock.uptimeMillis() - started; //又获取了一次系统时间,通过时间差,来控制循环。(当队列中数据好多时,才会用到) if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage())) { //如果此次循环已经超过规定时间,发送一个消息,会重新调用handlemessage方法。 throw new EventBusException("Could not send handler message"); } rescheduled = true; return; } } } finally { handlerActive = rescheduled; } }
我们再回到eventbus中看看invokeSubscriber方法
void invokeSubscriber(PendingPost pendingPost) { Object event = pendingPost.event; Subscription subscription = pendingPost.subscription; // 将封装在PendingPost中的数据取出来。 PendingPost.releasePendingPost(pendingPost);// 会将PendingPost对象中的数据清空,放到其内部的集合中,以便将来再用。 if (subscription.active) { invokeSubscriber(subscription, event);// 该方法,以前已经分析过了,就是通过反射回调到相应的方法中,完成post的实现。 } }
到这里,我们就看完了在主线程中实现回调的实现了。对于BackgroundPoster和AsyncPoster其实现是类似的,唯一的区别就是其是实现Runnable接口,其是运行在子线程中的。其中也是先将数据添加到一个队列中,然后再其run方法中调用eventBus.invokeSubscriber(pendingPost)方法。这两个的区别就是 一个线程安全,一个线程不安全。
到此。post方法我们也算分析完毕了。对于unregister()方法,如果前面的都看懂了,那么unregister就很简单了,就是将之前register时保存到集合中的数据删除掉就好了。这里就不再累赘了。相信大家都可以轻松看懂的。
- EventBus 2.4 源码分析
- EventBus源码注释分析
- EventBus框架源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus 源码分析
- EventBus 源码分析
- EventBus的源码分析
- EventBus 3 源码分析
- EventBus 源码分析
- EventBus的源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus源码分析
- EventBus 3.0 源码分析
- EventBus 3.0 源码分析
- EventBus源码分析
- Writer——FileWriter的使用示例
- linux-centos6.8mini-yum使用
- strcat函数实现
- MyBatis之传入参数
- SQL Server 2012笔记分享-49:理解数据库快照
- EventBus 源码分析
- MySQL 主从复制与读写分离概念及架构分析
- adaboost
- Android 入门----Activity生命周期
- MySQL基础九之存储引擎
- System中的常用方法
- 美团下拉菜单
- 浅谈SQL Server中的快照
- 使用canvas画出满天繁星