订阅(注册):EventBus的EventBus.getDefault().register(this);就是便利当前类的所有方法,寻找以onEvent开头的放大,以键值队的形式存储。
发布:EventBus.getDefault().post(param);
发布很简单就是调用这个方法,然后EventBus就会在内部存储的方法中扫描,找到参数匹配的就会调用反射去执行,它的内部就是维持一个Map集合,键就是当前类的class类型。然后根据你传入参数的类型进行查找相应的方法,你们觉得还是个事么?
过程就是在通过register注册,Map(当前类的class类型,)
下面我们通过源码来看下EventBus的执行流程。
public static EventBus getDefault() {
if(defaultInstance == null) {
Class var0 = EventBus.class;
synchronized(EventBus.class) {
if(defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
通过我们java基础可以知道,这样做的好处
1:为了防止并发多线程的访问我们加了同步,但这样会影响效率,因为每次走这里都得排队等待,比较慢。
2:为了解决上面每次进来都得排队等待的过程,通过两个非空判断,提高了执行效率
接着我们看下register都做了什么?
EventBus.getDefault().register(this);
public void register(Object subscriber) {
this.register(subscriber, "onEvent", false, 0);
}
private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {
List subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(), methodName);
Iterator var7 = subscriberMethods.iterator();
while(var7.hasNext()) {
SubscriberMethod subscriberMethod = (SubscriberMethod)var7.next();
this.subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
ubscriber 是我们扫描类的对象,也就是我们代码中常见的this;
methodName 这个是写死的:“onEvent”,用于确定扫描什么开头的方法,可见我们的类中都是以这个开头。
sticky 这个参数,解释源码的时候解释,暂时不用管
priority 优先级,优先级越高,在调用的时候会越先调用。
通过findSubscriberMethods可以看出传入一个class类型,以及一个方法的前缀,返回的List,肯定是去遍历该类的所有方法,根据传入的方法前缀去匹配,匹配成功后返回一个封装的list。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String eventMethodName) {
String key = subscriberClass.getName() + '.' + eventMethodName;
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.getMethods();
Method[] var13 = methods;
int var12 = methods.length;
for(int var11 = 0; var11 < var12; ++var11) {
Method method = var13[var11];
String methodName = method.getName();
if(methodName.startsWith(eventMethodName)) {
int modifiers = method.getModifiers();
if((modifiers & 1) != 0 && (modifiers & 1032) == 0) {
Class[] parameterTypes = method.getParameterTypes();
if(parameterTypes.length == 1) {
String modifierString = methodName.substring(eventMethodName.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(!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(!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 " + eventMethodName);
} else {
Map var25 = methodCache;
synchronized(methodCache) {
methodCache.put(key, var23);
return var23;
}
}
}
}
你只要记得一件事:扫描了所有的方法,把匹配的方法最终保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList )中;
eventType是我们方法参数的Class,Subscription中则保存着subscriber, subscriberMethod(method, threadMode, eventType), priority;包含了执行改方法所需的一切
register完毕后,知道了EventBus如何存储我们的方法的,下面我们看看post是如何调用我们的方法的。
由于之前我们知道eventbus通过map集合把我们的方法存储到了subscriptionsByEventType中,那么是post中肯定会去subscriptionsByEventType中去取方法然后调用。
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");
} else {
try {
while(!eventQueue.isEmpty()) {
this.postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
}
由第二三行代码可知道,当调研post的时候就回把当前线程的PostingThreadState存储到eventQueue中
把我们传入的event,保存到了当前线程中的一个变量PostingThreadState的eventQueue中。
10行:判断当前是否是UI线程。
16-18行:遍历队列中的所有的event,调用postSingleEvent(eventQueue.remove(0), postingState)方法。
这里大家会不会有疑问,每次post都会去调用整个队列么,那么不会造成方法多次调用么?
可以看到第7-8行,有个判断,就是防止该问题的,isPosting=true了,就不会往下走了。
下面再看下postSingleEvent()这个参数就是我们传入的实参。
然后根据这个
将我们的event,即post传入的实参;以及postingState传入到postSingleEvent中。
2-3行:根据event的Class,去得到一个List
private void postSingleEvent(Object event, EventBus.PostingThreadState postingState) throws Error {
Class eventClass = event.getClass();
List eventTypes = this.findEventTypes(eventClass);
boolean subscriptionFound = false;
int countTypes = eventTypes.size();
for(int h = 0; h < countTypes; ++h) {
Class clazz = (Class)eventTypes.get(h);
CopyOnWriteArrayList subscriptions;
synchronized(this) {
subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(clazz);
}
if(subscriptions != null && !subscriptions.isEmpty()) {
Iterator var11 = subscriptions.iterator();
while(var11.hasNext()) {
Subscription subscription = (Subscription)var11.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;
}
}
subscriptionFound = true;
}
}
if(!subscriptionFound) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
if(eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
this.post(new NoSubscriberEvent(this, event));
}
}
}
下面看如何执行反射
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch($SWITCH_TABLE$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);
}
}
直接反射调用;也就是说在当前的线程直接调用该方法;
case MainThread:
首先去判断当前如果是UI线程,则直接调用;否则: mainThreadPoster.enqueue(subscription, event);把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,然后执行的。
case BackgroundThread:
如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用
executorService = Executors.newCachedThreadPool();。
case Async:将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。
这么说BackgroundThread和Async有什么区别呢?
BackgroundThread中的任务,一个接着一个去调用,中间使用了一个布尔型变量handlerActive进行的控制。
Async则会动态控制并发。
到此,我们完整的源码分析就结束了,总结一下:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。分析这么久,一句话就说完了~~
其实不用发布者,订阅者,事件,总线这几个词或许更好理解,以后大家问了EventBus,可以说,就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。
到此,我们完整的源码分析就结束了,总结一下:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。分析这么久,一句话就说完了~~
其实不用发布者,订阅者,事件,总线这几个词或许更好理解,以后大家问了EventBus,可以说,就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。