好不啰嗦,直奔主题,我打算用三篇文章来简单跟大家一起过一遍eventbus框架的源码,三篇分别分为注册、通知、注销三个主要内容点来展开,所以这篇咱们来了解一下注册,看看eventbus是怎样处理我们的订阅者的。
(本文建立在3.1.1版本下)
首先上个注册方式
EventBus.getDefault().register(this)
然后生成我们的订阅方法(就是接收数据的方法)
@Subscribe(threadMode = ThreadMode.MAIN)
fun onGetMessage(messageAEvent: MessageEvent.Companion.MessageAEvent){
}
是的,这样就算是注册好了,当其他界面调用了post方法后就能接收到通知数据了(传入了一样的参数类型,比如栗子的MessageAEvent对象),比如这样
EventBus.getDefault().post(MessageEvent.Companion.MessageAEvent(1))
那为啥这么流弊的就可以接收到了呢,而且还是两个不想关的界面,只需要一个条件,就是使用相同的参数类型,简直了,不要急,让我们一步步开始迷失在源码的海洋里吧。
首先我们看一下getDefault()吧
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
哦豁,一个单例生成了EventBus对象,课外知识(敲黑板)这个单例模式叫《双重校验锁单例模式》,单例模式可不止是判断一下是否为空一种写法,出于多线程安全方面需要这样写,关键词synchronized 、volatile ,有兴趣的筒子网上搜单例模式各种写法的利弊,开开眼界哈,那我们继续,new EventBus()
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
哦豁,传入了一个builder,课外知识(敲黑板)这种模式叫建造者模式,日常生活使用超多,比如创建Dialog,老规矩,有兴趣自己搜哈,那回到一开始 ,我们要去.register(this)看看了
public void register(Object subscriber) {
1 Class<?> subscriberClass = subscriber.getClass();
2 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
3 subscribe(subscriber, subscriberMethod);
}
}
}
1:获得我们的订阅类(activity之类的)
2:获得我们订阅类里的订阅方法(加了注解用来接收通知的那个方法(可能多个所以是list))
3:(敲黑板)将两者绑定起来,提供给通知的时候调用
好讲完了,下课。
说笑的说笑的,收起你们的40米大刀,我们继续,继续!现在我们要去2那里,findSubscriberMethods
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
1 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
2 subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
3 METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
1:根据订阅类去搜索出订阅方法(第一次所以为空)
2:根据ignoreGeneratedIndex值判断调用什么方法去寻找订阅的方法s(是在builder里面设置的,表示是否使用生成的APT代码去优化查找事件的过程,如果项目中没有接入EventBus的APT,也可以将其设置为false)
3:将查询到的订阅方法保存起来
好的,接下来我们要看findUsingInfo(subscriberClass)
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
1 findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
2 findUsingReflectionInSingleClass(findState);
}
3 findState.moveToSuperclass();
}
4 return getMethodsAndRelease(findState);
}
1:根据订阅类初始化一个FindState对象(主要作用是放东西的)
2:第一次进入findState.subscriberInfo是空的,还没查呢能有东西嘛,所以会执行findUsingReflectionInSingleClass,通过反射查找订阅方法
3:继续遍历父类(所以你子类跟父类的订阅方法,都是有效的)
4:在findState拿出查找到的订阅方法返回出去,并将findState释放掉
ok,接下来要看的是2,findUsingReflectionInSingleClass(findState)
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
1 methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
2 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
3 if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
4 if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
5 if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
6 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
别看这个方法pia一大堆,其实也就那回事
1:通过反射获取所有的方法
2、3、4:根据条件去判断合适的方法,首先是要Public的,然后不能有abstract、static等鬼东西,接着只能有一个参数,接着需要有@Subscribe注解
5:校验是否可以加入到list中
6:生成一个合格的SubscriberMethod对象(订阅方法)然后add进findState的subscriberMethods集合里
好了,到这里过,我们已经把订阅类里的合格的订阅方法查了出来,并丢进了findState类里,下一步我们要去上面上面的getMethodsAndRelease(findState)
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
这里没啥,就是新建个list,把刚刚查的方法丢进去,然后返回出去并让findState滚
接下来我们回到外面的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);
}
}
}
此时subscriberMethods 已经有值了,接下来要去走subscribe(subscriber, subscriberMethod)
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
1 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 i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
2 typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
3 if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
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();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
这里可能会把你绕晕,解释之前我先说两个理论知识
首先就是,我们的订阅类跟订阅方法是一对多的关系,而我们订阅方法里传的参数跟订阅类也是一对多的关系(比如我一个EventObject,任何一个类都能拿去使用到订阅方法里当参数),所以我们的关系是:
参数:类:方法 = 1:N:N,应该可以理解吧,所以这个方法里subscriptionsByEventType对象是这样的:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
1:以eventType(参数)为key,subscriptions(Subscription的集合)为value保存起来
2:以subscriber(订阅类)为key,subscribedEvents(参数的集合)为value保存起来
3:如果是粘性事件的话(不打算讲这个,因为又可以单独开一篇,简单来讲就是当你post出消息之后,订阅者才订阅,如果设置了粘性事件,那他是可以收到的,一般我们都是先执行好了订阅者,再post消息对吧,这个反着来,也可以收到消息就对了)
这个方法就是一个数据绑定的地方(把他们按照我说的这个关系给存放了起来,提供给post的时候使用,至于怎么使用的第二篇说)
来总结一下大致流程
1 首先获得我们的订阅者对象
2 利用反射的原理去获取我们订阅者的方法,之后根据条件去寻找合格的方法并保存(public、非静态、一个传参、拥有@Subscribe注解等)
3 遍历完子类会继续遍历父类,所以子类父类的订阅方法都会生效
4 遍历我们寻找到的合格的方法集合,让它们跟着订阅者一起去做一个数据存放(绑定)的流程,其中一个订阅者可以拥有多个订阅方法,一个参数类型(event)也可以有多个订阅者使用
5 注册流程结束
ok,走到这儿我们的第一部分register已经完成了他的工作,接下来就是触发post的时候,下一篇见。