EventBus的源码解析(一)
2017-02-21 13:57
337 查看
EventBus已经出来很久了,相信大家并不陌生,很多android程序员应该在项目中都使用过它了,当然了我也使用过,以前使用的时候就是觉得它使用起来挺简单的,但是它的具体实现原理却没有研究过,作为一名有追求的程序员,仅仅会使用是无法满足我的,今天我们就从源码来看看他是如何实现的。
EventBus的作用就是方便应用的各个部分之间的通信,且能降低彼此之间的耦合。
使用EventBus第一步就是要获取它的实例,看一下它提供的方法:
EventBus.getDefault()
可以看到他使用的是双重判断的单列模式,并且考虑了并发问题。
使用EventBus主要分为两步:
1. 注册 Register(Object subscriber)
首先看方法的几个参数,目前看得懂的就是Object subscriber,因为这是我们register(param)传进去。
让我们来看看
参数subscriber.getClass()返回的是subscriber这个对象所属的类对象(java.lang.Class),这个对象是在类加载过程中生成的,它主要是作为方法区中这个类的各个数据的访问入口(小说一句,该对象存在于HotSpot虚拟机中的方法区里面)。
2-10行:通过我们传入对象的类名作为键在一个map集合中查找对应的值,如果查找到了就直接返回。
22-23行:通过我们之前获得的java.lang.Class类对象利用反射去获取该类里面所有的方法。
26-66行:遍历该类中所有的方法,如果是以“onEvent”开头,并且该方法的修饰符是public且非static和abstract方法,并且该方法只有一个参数,就可以进入到封装的代码中。通过截取该方法名onEvent后面的字符串与“MainThread”,“BackgroundThread”,“Async”进行对比来确定threadMode,它是一个枚举,eventType是该方法的参数Class对象,将该方法的method,threadMode,eventType作为参数封装到一个SubscriberMethod对象当中,并将该对象添加到一个ArrayList集合当中并返回,在返回之前会吧这个以传入的类名作为键,这个ArrayList集合作为值缓存到methodCache这个map集合中,下次就可以直接从缓存中拿到ArrayList对象了。
接下来就是继续遍历我们刚才获取到的ArrayList对象,将得到的subscriberMethod 对象作为参数传入subscribe(subscriber, subscriberMethod, sticky, priority)这个方法,我们再去看看这个方法里面做了什么:
还好这个方法没有那么长,让我们来分析一下:
首先创建了一个Subscription对象,将之前封装对象里面的eventType(方法参数的Class 对象)作为键,创建一个List集合作为值添加到了一个concurrentHashmap对象subscriptionsByEventType中,后序就是通过这个对象来查找我们想要调用的方法并通过反射调用(这里只是先提一下)。
之前有几个参数我们没看懂,比如priority,当传入相同参数类型有多个方法的时候,将刚生产的Subscription按优先级的大小来确定位置添加到List集合当中。
将方法参数的Class对象添加到var14 就是一个List集合中,并将这个List集合作为值和传入的subscriber作为键添加到一个map集合中typesBySubscriber。
2. 发布 Post(Object event)
让我们来分析一下发布方法里面做了些什么:
1.将post(Object event)参数event对象添加到一个list集合。
2.第6行判断当前是否是UI线程,并且把isPosting属性设置为true。
3.然后会走到一个循环当中 当list集合不为空,会调用postSingleEvent方法,好吧 我们去看下这个方法。
4.最后会吧 isPosting和isMainThread都设置为false。
首先看到这个方法lookupAllEventTypes(eventClass)
这个方法会得到一个List集合,里面装的是我们pos传入参数event对象的Class,以及他的父类和接口的Class对象。
接着会遍历这个List集合,获得其中的每一个Class对象,然后调用了postSingleEventForEventType(event, postingState, eventClass)方法,再进入这个方法
subscriptionsByEventType 有没有很熟悉啊??? 对了就是之前那个存放方法参数的Class对象为键,Subscriptions为值得那个concurrentHashmap对象,这个通过这个对象将post(Object event)参数的Class对象作为键的值取出来,这如果这个event的Class对象跟之前那个方法参数的Class对象是同一个对象,那么取出来的不就是之前存的Subscriptions对象吗?而Subscriptions集合里面装着newSubscription对象,newSubscription对象里面封装了所有需要的信息,在看postToSubscription(subscription, event, postingState.isMainThread)方法
当threadMode枚举为1是说明找到的是onEvent()方法,并利用反射调用;当threadMode为2的时候说明找到的是onEventMainThread()方法,然后判断是否是主线程,如果是主线程就反射调用,如果不是,就将放入队列中。当threadMode为3的时候说明找到的是onEventBackgroundThread()方法,同样判断发送事件如果是在主线程,就将其放入队列,如果是子线程就直接利用发射调用,当threadMode为4的时候说明是调用onEventAsync()方法,它会将其放入队列并在合适的时候反射调用。
好了,到这里EventBus的源码大致分析清楚了,但还有一小部分没有分析,比如boolean sticky这个参数是干什么用的?registerSticky()这个构造函数有什么不同?下次再跟大家一起分析了。
EventBus的作用就是方便应用的各个部分之间的通信,且能降低彼此之间的耦合。
使用EventBus第一步就是要获取它的实例,看一下它提供的方法:
EventBus.getDefault()
public static EventBus getDefault() { if(defaultInstance == null) { Class var0 = EventBus.class; synchronized(EventBus.class) { if(defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
可以看到他使用的是双重判断的单列模式,并且考虑了并发问题。
使用EventBus主要分为两步:
1. 注册 Register(Object subscriber)
public void register(Object subscriber) { this.register(subscriber, false, 0); }
private synchronized void register(Object subscriber, boolean sticky, int priority) { List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); Iterator i$ = subscriberMethods.iterator(); while(i$.hasNext()) { SubscriberMethod subscriberMethod = (SubscriberMethod)i$.next(); this.subscribe(subscriber, subscriberMethod, sticky, priority); } }
首先看方法的几个参数,目前看得懂的就是Object subscriber,因为这是我们register(param)传进去。
让我们来看看
List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
参数subscriber.getClass()返回的是subscriber这个对象所属的类对象(java.lang.Class),这个对象是在类加载过程中生成的,它主要是作为方法区中这个类的各个数据的访问入口(小说一句,该对象存在于HotSpot虚拟机中的方法区里面)。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { String key = subscriberClass.getName(); Map clazz = methodCache; List subscriberMethods; synchronized(methodCache) { subscriberMethods = (List)methodCache.get(key); } if(subscriberMethods != null) { return subscriberMethods; } else { ArrayList var23 = new ArrayList(); Class var24 = subscriberClass; HashSet eventTypesFound = new HashSet(); for(StringBuilder methodKeyBuilder = new StringBuilder(); var24 != null; var24 = var24.getSuperclass()) { String name = var24.getName(); if(name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) { break; } Method[] methods = var24.getDeclaredMethods(); Method[] arr$ = methods; int len$ = methods.length; for(int i$ = 0; i$ < len$; ++i$) { Method method = arr$[i$]; String methodName = method.getName(); if(methodName.startsWith("onEvent")) { int modifiers = method.getModifiers(); if((modifiers & 1) != 0 && (modifiers & 5192) == 0) { Class[] parameterTypes = method.getParameterTypes(); if(parameterTypes.length == 1) { String modifierString = methodName.substring("onEvent".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")) { if(!this.skipMethodVerificationForClasses.containsKey(var24)) { throw new EventBusException("Illegal onEvent method, check for typos: " + method); } continue; } threadMode = ThreadMode.Async; } Class eventType = parameterTypes[0]; methodKeyBuilder.setLength(0); methodKeyBuilder.append(methodName); methodKeyBuilder.append('>').append(eventType.getName()); String methodKey = methodKeyBuilder.toString(); if(eventTypesFound.add(methodKey)) { var23.add(new SubscriberMethod(method, threadMode, eventType)); } } } else if(!this.skipMethodVerificationForClasses.containsKey(var24)) { Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + var24 + "." + methodName); } } } } if(var23.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " + "onEvent"); } else { Map var25 = methodCache; synchronized(methodCache) { methodCache.put(key, var23); return var23; } } } }
2-10行:通过我们传入对象的类名作为键在一个map集合中查找对应的值,如果查找到了就直接返回。
22-23行:通过我们之前获得的java.lang.Class类对象利用反射去获取该类里面所有的方法。
26-66行:遍历该类中所有的方法,如果是以“onEvent”开头,并且该方法的修饰符是public且非static和abstract方法,并且该方法只有一个参数,就可以进入到封装的代码中。通过截取该方法名onEvent后面的字符串与“MainThread”,“BackgroundThread”,“Async”进行对比来确定threadMode,它是一个枚举,eventType是该方法的参数Class对象,将该方法的method,threadMode,eventType作为参数封装到一个SubscriberMethod对象当中,并将该对象添加到一个ArrayList集合当中并返回,在返回之前会吧这个以传入的类名作为键,这个ArrayList集合作为值缓存到methodCache这个map集合中,下次就可以直接从缓存中拿到ArrayList对象了。
List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); Iterator i$ = subscriberMethods.iterator(); while(i$.hasNext()) { SubscriberMethod subscriberMethod = (SubscriberMethod)i$.next(); this.subscribe(subscriber, subscriberMethod, sticky, priority); }
接下来就是继续遍历我们刚才获取到的ArrayList对象,将得到的subscriberMethod 对象作为参数传入subscribe(subscriber, subscriberMethod, sticky, priority)这个方法,我们再去看看这个方法里面做了什么:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { Class eventType = subscriberMethod.eventType; CopyOnWriteArrayList subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventType); Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); if(subscriptions == null) { subscriptions = new CopyOnWriteArrayList(); this.subscriptionsByEventType.put(eventType, subscriptions); } else if(subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } int size = subscriptions.size(); for(int subscribedEvents = 0; subscribedEvents <= size; ++subscribedEvents) { if(subscribedEvents == size || newSubscription.priority > ((Subscription)subscriptions.get(subscribedEvents)).priority) { subscriptions.add(subscribedEvents, newSubscription); break; } } Object var14 = (List)this.typesBySubscriber.get(subscriber); if(var14 == null) { var14 = new ArrayList(); this.typesBySubscriber.put(subscriber, var14); } ((List)var14).add(eventType); if(sticky) { Map var11 = this.stickyEvents; Object stickyEvent; synchronized(this.stickyEvents) { stickyEvent = this.stickyEvents.get(eventType); } if(stickyEvent != null) { this.postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); } } }
还好这个方法没有那么长,让我们来分析一下:
首先创建了一个Subscription对象,将之前封装对象里面的eventType(方法参数的Class 对象)作为键,创建一个List集合作为值添加到了一个concurrentHashmap对象subscriptionsByEventType中,后序就是通过这个对象来查找我们想要调用的方法并通过反射调用(这里只是先提一下)。
之前有几个参数我们没看懂,比如priority,当传入相同参数类型有多个方法的时候,将刚生产的Subscription按优先级的大小来确定位置添加到List集合当中。
Object var14 = (List)this.typesBySubscriber.get(subscriber); if(var14 == null) { var14 = new ArrayList(); this.typesBySubscriber.put(subscriber, var14); } ((List)var14).add(eventType); if(sticky) { Map var11 = this.stickyEvents; Object stickyEvent; synchronized(this.stickyEvents) { stickyEvent = this.stickyEvents.get(eventType); } if(stickyEvent != null) { this.postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); } }
将方法参数的Class对象添加到var14 就是一个List集合中,并将这个List集合作为值和传入的subscriber作为键添加到一个map集合中typesBySubscriber。
2. 发布 Post(Object event)
public void post(Object event) { EventBus.PostingThreadState postingState = (EventBus.PostingThreadState)this.currentPostingThreadState.get(); List eventQueue = postingState.eventQueue; eventQueue.add(event); 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 { while(!eventQueue.isEmpty()) { this.postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }
让我们来分析一下发布方法里面做了些什么:
1.将post(Object event)参数event对象添加到一个list集合。
2.第6行判断当前是否是UI线程,并且把isPosting属性设置为true。
3.然后会走到一个循环当中 当list集合不为空,会调用postSingleEvent方法,好吧 我们去看下这个方法。
4.最后会吧 isPosting和isMainThread都设置为false。
private void postSingleEvent(Object event, EventBus.PostingThreadState postingState) throws Error { Class eventClass = event.getClass(); boolean subscriptionFound = false; if(this.eventInheritance) { List eventTypes = this.lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); for(int h = 0; h < countTypes; ++h) { Class clazz = (Class)eventTypes.get(h); subscriptionFound |= this.postSingleEventForEventType(event, postingState, clazz); } } else { subscriptionFound = this.postSingleEventForEventType(event, postingState, eventClass); } if(!subscriptionFound) { if(this.logNoSubscriberMessages) { Log.d(TAG, "No subscribers registered for event " + eventClass); } if(this.sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { this.post(new NoSubscriberEvent(this, event)); } } }
首先看到这个方法lookupAllEventTypes(eventClass)
private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) { Map var2 = eventTypesCache; synchronized(eventTypesCache) { Object eventTypes = (List)eventTypesCache.get(eventClass); if(eventTypes == null) { eventTypes = new ArrayList(); for(Class clazz = eventClass; clazz != null; clazz = clazz.getSuperclass()) { ((List)eventTypes).add(clazz); addInterfaces((List)eventTypes, clazz.getInterfaces()); } eventTypesCache.put(eventClass, eventTypes); } return (List)eventTypes; } }
这个方法会得到一个List集合,里面装的是我们pos传入参数event对象的Class,以及他的父类和接口的Class对象。
接着会遍历这个List集合,获得其中的每一个Class对象,然后调用了postSingleEventForEventType(event, postingState, eventClass)方法,再进入这个方法
private boolean postSingleEventForEventType(Object event, EventBus.PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList subscriptions; synchronized(this) { subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventClass); } if(subscriptions != null && !subscriptions.isEmpty()) { Iterator i$ = subscriptions.iterator(); while(i$.hasNext()) { Subscription subscription = (Subscription)i$.next(); postingState.event = event; postingState.subscription = subscription; boolean aborted = false; try { this.postToSubscription(subscription, event, postingState.isMainThread); aborted = postingState.canceled; } finally { postingState.event = null; postingState.subscription = null; postingState.canceled = false; } if(aborted) { break; } } return true; } else { return false; } }
subscriptionsByEventType 有没有很熟悉啊??? 对了就是之前那个存放方法参数的Class对象为键,Subscriptions为值得那个concurrentHashmap对象,这个通过这个对象将post(Object event)参数的Class对象作为键的值取出来,这如果这个event的Class对象跟之前那个方法参数的Class对象是同一个对象,那么取出来的不就是之前存的Subscriptions对象吗?而Subscriptions集合里面装着newSubscription对象,newSubscription对象里面封装了所有需要的信息,在看postToSubscription(subscription, event, postingState.isMainThread)方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch(EventBus.SyntheticClass_1.$SwitchMap$de$greenrobot$event$ThreadMode[subscription.subscriberMethod.threadMode.ordinal()]) { case 1: this.invokeSubscriber(subscription, event); break; case 2: if(isMainThread) { this.invokeSubscriber(subscription, event); } else { this.mainThreadPoster.enqueue(subscription, event); } break; case 3: if(isMainThread) { this.backgroundPoster.enqueue(subscription, event); } else { this.invokeSubscriber(subscription, event); } break; case 4: this.asyncPoster.enqueue(subscription, event); break; default: throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); } }
当threadMode枚举为1是说明找到的是onEvent()方法,并利用反射调用;当threadMode为2的时候说明找到的是onEventMainThread()方法,然后判断是否是主线程,如果是主线程就反射调用,如果不是,就将放入队列中。当threadMode为3的时候说明找到的是onEventBackgroundThread()方法,同样判断发送事件如果是在主线程,就将其放入队列,如果是子线程就直接利用发射调用,当threadMode为4的时候说明是调用onEventAsync()方法,它会将其放入队列并在合适的时候反射调用。
好了,到这里EventBus的源码大致分析清楚了,但还有一小部分没有分析,比如boolean sticky这个参数是干什么用的?registerSticky()这个构造函数有什么不同?下次再跟大家一起分析了。
相关文章推荐
- EventBus 利弊与源码解析
- 开源项目源码解析-EventBus 源码解析
- [EventBus源码解析] EventBus.register 方法详述
- Android EventBus源码解析
- [EventBus源码解析] 订阅者处理消息的四种ThreadMode
- Android EventBus源码解析 带你深入理解EventBus
- Android EventBus源码解析 带你深入理解EventBus
- EventBus源码解析
- EventBus 源码解析
- EventBus(2)——源码解析
- EventBus 源码解析
- Android EventBus源码解析 带你深入理解EventBus
- EventBus 源码解析
- [EventBus源码解析] 初探EventBus
- Android EventBus源码解析 带你深入理解EventBus
- EventBus源码解析 带你深入理解EventBus
- EventBus---EventBus源码解析 带你深入理解EventBus
- EventBus 源码解析(一)
- Android EventBus源码解析 带你深入理解EventBus
- Android 源码解析:EventBus