EventBus的使用,同时本文也是基于3.1.1版来进行讲解
首先关联EventBus的库
implementation 'org.greenrobot:eventbus:3.1.1'
1.事件订阅
EventBus.getDefault().register(this);
2.事件发布
EventBus.getDefault().post(new MessageEventBean("shoft"));
3.接收事件(订阅方法)
@Subscribe(threadMode = ThreadMode.MAIN)
public void threadMain(MessageEventBean messageEventBean){
tv.setText(messageEventBean.getMsg());
}
4.取消订阅
EventBus.getDefault().unregister(this);
以上就是EventBus的简单使用。
首先我们来分析事件订阅,看看它里面到底干了些啥。。打开EventBus#getDefault()方法,发现它是DCL双重锁机制的单例模式,这种模式保证了其在同一个进程中的访问是安全的。紧接着跟踪new EventBus(),发现最终实现了EventBus自己的的一个构造方法,并在里面进行了大量的初始化工作。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus(); //=============1
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
//进行了大量的初始化操作
EventBus(EventBusBuilder builder) { //=============2
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;
}
以上就是EventBus#getDefault()方法所作的一些工作,接下来分析EventBus#register(this)方法,register方法中首先会获取到订阅类的类型,然后再去获取当前订阅类中的所有订阅方法,最后循环遍历订阅方法,完成订阅。。
ublic void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass(); //1.获取到订阅类的类型
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//2.获取到当前订阅类中的所有订阅的方法,即接收事件的方法。
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);//3.循环订阅方法,完成订阅
}
}
}
接下来,我们来分析它是怎样来获取到当前订阅类中的所有订阅的方法,即查看EventBus#findSubscriberMethods(Class> subscriberClass)方法,把当前订阅类型subscriberClass作为参数传入,发现其获取订阅方法数组的方式有两种,一种是findUsingReflection(subscriberClass),称其为方法一,另一中是findUsingInfo(subscriberClass),称其为方法二。
List findSubscriberMethods(Class> subscriberClass) {
//从METHOD_CACHE这个ConcurrentHashMap集合中取到所有订阅方法的数组
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
//若subscriberMethods有值,则直接返回
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex值默认为false,以下是两种获取订阅方法数组的方式,我们称其为方法一、方法二。
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass); //====方法一
} else {
subscriberMethods = findUsingInfo(subscriberClass); //====方法二
}
//判断订阅类中是否有订阅方法,若无,抛出异常,若存在,则把所有订阅方法作为数组存入METHOD_CACHE集中,并返回订阅方法数组
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
方法一findUsingReflection(subscriberClass)中,传入subscriberClass作为参数,方法中,首先会准备一个FindState类来保存订阅类的信息,紧接着初始化订阅类,并赋值给clazz,然后通过while循环来遍历添加当前订阅类及其父类的所有订阅方法(默认会去查询父类的订阅方法)。最后释放FindState中订阅类的信息,并返回订阅类中的订阅方法数组。
private List findUsingReflection(Class> subscriberClass) {
//准备一个FindState,里面保存了订阅类的信息
FindState findState = prepareFindState();
//初始化FindState
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//这个方法才是获取到订阅方法数组的关键
findUsingReflectionInSingleClass(findState);
//往父类查找
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);//释放FindState中保存的所有订阅类的信息,并返回订阅方法数组
}
这里比较关键的就是订阅方法添加的这个方法findUsingReflectionInSingleClass,接下来继续讲解findUsingReflectionInSingleClass方法,这个方法中首先获取订阅类中的所有方法,然后循环遍历出订阅方法,最后把订阅方法的信息全部添加到subscriberMethods数组中。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
//获取当前订阅类中的所有方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
//循环遍历当前订阅类中的所有方法
for (Method method : methods) {
//获取方法修饰符
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //获取方法参数类型:只允许一位参数
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//获取当前方法是否存在Subscribe注解,即方法是否订阅
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//获取当前订阅方法的参数类型
Class> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {//判断是否添加过
//获取当前订阅方法的线程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
//把订阅方法及其参数、线程模式、进程优先级作为SubscriberMethod实体加入subscriberMethods数组中
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");
}
}
}
到了这里,方法一的订阅方法数组就已经存在了。。
方法二findUsingInfo(subscriberClass)中,内容与方法一相似,不过这里多了一个SubscriberInfo接口类,它主要是用来存储订阅相关信息,如果SubscriberInfo接口中的订阅信息接口被实现过,则会从SubscriberInfo接口中取出订阅方法,并添加到subscriberMethods数组中,如果SubscriberInfo未被实例化或者接口未被实现过,则还是通过方法一来进行添加订阅方法。。。
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//初始化subscriberInfo接口类
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
//获取订阅方法数组
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
//循环添加订阅方法到subscriberMethods数组中
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState); //同方法一
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
到此为止,订阅方法数组的添加都已讲解完毕。。
上面把订阅类中所有的订阅方法都加入到了数组中,接下来就对这些订阅方法与订阅类进行订阅。subscribe(subscriber, subscriberMethod),订阅类与订阅方法作为参数传入。把订阅方法参数类型作为KEY,订阅类和订阅方法作为VALUE存入subscriptionsByEventType集合,同时把subscriber(订阅类)为KEY,subscribedEvents(参数类型数组)为VALUE存入typesBySubscriber集合中,这样就把订阅类、订阅方法、参数类型进行了订阅,
需要说明的是,当订阅方法中设置了粘滞属性sticky为true时,若stickyEvents集合中保存了参数值,以及参数对象,则最终会执行invokeSubscriber(subscription, event)方法,这个方法时通过反射来调用指定的订阅方法。在这里,很好地解释了Eventbus的sticky,它可以先发送,后接收。。。
// 必须同步调用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;//获取订阅方法参数类型
//把订阅类和订阅方法作为参数传入Subscription,得到Subscription的实例
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//在subscriptionsByEventType集合,eventType(参数类型)作为KEY,Subscription实例作为VALUE,进行绑定。
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
//同一个订阅类中有相同的订阅方法
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
//直接把Subscription的实例newSubscription添加,或根据优先级来进行添加
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//在typesBySubscriber集合中,subscriber(订阅类)为KEY,subscribedEvents(参数类型数组)为VALUE,进行绑定。
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//判断当前订阅方法中sticky是否为true,默认为false
if (subscriberMethod.sticky) {
if (eventInheritance) {//默认为true
// 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).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
//该方法中打开会进入到postToSubscription(newSubscription, stickyEvent, isMainThread())这个方法,继续打开会进入到postToSubscription方法中,这里有五种线程模式,针对订阅方法中线程模式的不同,相应的处理方式也会有所差异,不过其最终调用的核心是invokeSubscriber(subscription, event)方法,这个方法有一段代码subscription.subscriberMethod.method.invoke(subscription.subscriber, event);通过反射来调用指定的方法。。我觉得这段代码才是整个EventBus的核心。。
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
好了,事件订阅的流程,到此为止
EventBus.getDefault().post(new MessageEventBean("shoft"));
EventBus.getDefault()的解释同上,那么接下来讲解post(new MessageEventBean(“shoft”))方法,初始化了一个PostingThreadState的对象,发布事件的一些相关信息都存在PostingThreadState类中,事件只能同步发送,一次只发送一个事件,
public void post(Object event) {
//初始化PostingThreadState
PostingThreadState postingState = currentPostingThreadState.get();
List
发送单个事件postSingleEvent(eventQueue.remove(0), postingState);传入了一个参数对象以及PostingThreadState实例,它里面有一个postSingleEventForEventType(event, postingState, clazz)方法,
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//获取参数类型
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {//eventInheritance默认为true
//获取当前参数类型及其父类的数组
List> eventTypes = lookupAllEventTypes(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);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
//没有订阅类
post(new NoSubscriberEvent(this, event));
}
}
}
打开查看其源码,发现它会从subscriptionsByEventType集合中取值,这个集合在事件订阅的时候就已经生成了,我么需要关注的是postToSubscription(subscription, event, postingState.isMainThread);这个方法,其它的都是赋值。。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) {
//从subscriptionsByEventType集合中,根据参数类型取出subscriptions数组,里面保存着订阅类、订阅方法。。
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
//赋值
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
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(subscription, event, postingState.isMainThread)这个方法,方法中有五种线程模式,分别是POSTING、MAIN、MAIN_ORDERED、BACKGROUND、ASYNC,在这里,很明显,核心代码只有一句
invokeSubscriber(subscription, event),在这句代码中,事件的发布才真正完成。
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);
}
}
打开invokeSubscriber(subscription, event)方法,发现它里面非常简洁,只有一句代码,当然我指的是有效的,
就是这句subscription.subscriberMethod.method.invoke(subscription.subscriber, event),它采用了反射机制,通过反射的方式,调用了指定的订阅方法。。到了这里,我们就可以明白了EventBus的工作机制。。
void invokeSubscriber(Subscription subscription, Object event) {
try {
//通过反射的方式,调用指定的方法。。
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
总结:事件的发布,其发送方式是同步的,只有当一个事件发送成功,才会发送下一个事件。然后通过反射机制,调起之前订阅过的方法。。这样消息就完美的进行了传递。说到底,EventBus就是通过反射来进行消息传递的。
EventBus.getDefault().unregister(this);
关闭订阅类时,需要把相关的订阅信息给清除掉,那查看unregister(this)方法,在该方法中主要是解绑,即用来移除在事件订阅时绑定的一些订阅相关的数据。明显点就是清除在subscriptionsByEventType集合中保存的指定的订阅方法参数。以及清除在typesBySubscriber集合中指定的订阅类。。
public synchronized void unregister(Object subscriber) {
//从typesBySubscriber集合中,根据订阅类,取出当前订阅类中的订阅方法参数类型数组。
List> 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());
}
}
//移除数据
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
//从subscriptionsByEventType集合中,根据参数类型,取出订阅类以及订阅方法的实例的数组subscriptions
List subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
//逐一移除参数类型
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
总结:取消订阅,通俗点讲就是清除事件订阅时保存的数据,,防止订阅类关闭时,造成内存泄漏。
以上就是EventBus的原理解析流程,讲的有误的地方希望大家留言指出,谢谢。。