rocketmq还要eventbus_EventBus源码详解,看这一篇就够了

之前写过一篇关于EventBus的文章,大家的反馈还不错(EventBus3.0使用详解),如果你还没有使用过EventBus,可以去那篇文章看看。当时刚接触EventBus,对它的理解也仅仅是停留在表面,写那篇文章也是记录下EventBus的一个简单的使用的过程,而如今随着EventBus在项目中的多次运用,个人感觉,会用一个框架跟知道它的原理这是两码事,所以下定决心写这篇文章来帮助自己更好的理解EventBus。

在进入主题之前,我们先保持着这样几个疑问,EventBus的使用三要素里,我们为什么要去定义事件方法,并且用到了@subscribe()注解? EventBus.getDefault().register(Object)这行代码到底干了什么?发送事件的时候又做了哪些操作?为什么要在onDestory()做解除绑定的操作...等等

(一) 注册: EventBus.getDefault().register(obj)

首先调用EventBus的静态getDefault()方法返回一个EventBus对象,采用单例实现。

// 静态的单例

static volatile EventBus defaultInstance;

// 默认的EventBusBuilder对象

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

// 使用了单例的双重锁机制

public static EventBus getDefault() {

if (defaultInstance == null) {

synchronized (EventBus.class) {

if (defaultInstance == null) {

defaultInstance = new EventBus();

}

}

}

return defaultInstance;

}

// 调用带参数的构造方法

public EventBus() {

this(DEFAULT_BUILDER);

}

//

EventBus(EventBusBuilder builder) {

//...

subscriptionsByEventType = new HashMap<>();

typesBySubscriber = new HashMap<>();

stickyEvents = new ConcurrentHashMap<>();

mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);

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(builder)构造方法里初始化了一些配置信息。

之后调用了EventBus的register(obj)方法,这个方法接收的参数类型是Object。这里我们分步骤1和步骤2去看看做了哪些操作。

public void register(Object subscriber) {

// 通过反射拿到传入的obj的Class对象,如果是在MainActivity里做的注册操作,

// 那subscriber就是MainActivity对象

Class> subscriberClass = subscriber.getClass();

// 步骤1

List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

// 步骤2

synchronized (this) {

for (SubscriberMethod subscriberMethod : subscriberMethods) {

subscribe(subscriber, subscriberMethod);

}

}

}

步骤1

List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

首先subscriberMethodFinder对象是在EventBus带参数的构造函数里进行初始化的,从这个findSubscriberMethods()方法名就可以看出来,步骤1的是去获取当前注册的对象里所有的被@Subscribe注解的方法集合,那这个List集合的对象SubscriberMethod又是什么东东呢? 我们去看一下

public class SubscriberMethod {

final Method method;

final ThreadMode threadMode;

final Class> eventType;

final int priority;

final boolean sticky;

/** Used for efficient comparison */

String methodString;

public SubscriberMethod(Method method, Class> eventType, ThreadMode threadMode, int priority, boolean sticky) {

this.method = method;

this.threadMode = threadMode;

this.eventType = eventType;

this.priority = priority;

this.sticky = sticky;

}

}

...

看完这个类里定义的信息,大概明白了。SubscriberMethod 定义了Method方法名,ThreadMode 线程模型,eventType 事件的class对象,priority是指接收事件的优先级,sticky是指是否是粘性事件,SubscriberMethod 对这些信息做了一个封装。这些信息在我们处理事件的时候都会用到:

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)

public void XXX(MessageEvent messageEvent) {

...

}

好的,知道了SubscriberMethod 是什么东东后,我们直接进入findSubscriberMethods(subscriberClass)方法。

List findSubscriberMethods(Class> subscriberClass) {

// METHOD_CACHE是事先定义了一个缓存Map,以当前的注册对象的Class对象为key,注册的对象里所有的被@Subscribe注解的方法集合为value

List subscriberMethods = METHOD_CACHE.get(subscriberClass);

// 第一次进来的时候,缓存里面没有集合,subscriberMethods 为null

if (subscriberMethods != null) {

return subscriberMethods;

}

// ignoreGeneratedIndex是在SubscriberMethodFinder()的构造函数初始化的,默认值是 false

if (ignoreGeneratedIndex) {

// 通过反射去获取

subscriberMethods = findUsingReflection(subscriberClass);

} else {

// 通过apt插件生成的代码。使用subscriber Index生成的SubscriberInfo来获取订阅者的事件处理函数,

subscriberMethods = findUsingInfo(subscriberClass);

}

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;

}

}

首先是从缓存中获取,如果缓存中存在直接返回,这里使用缓存有一个 好处,比如在MainActivity进行了注册操作,多次启动MainActivity,就会直接去缓存中拿数据。如果缓存里没有数据,就会根据ignoreGeneratedIndex 这个boolean值去调用不同的方法,ignoreGeneratedIndex 默认为false。如果此时,获取到的方法集合还是空的,程序就会抛出异常,提醒用户被注册的对象以及他的父类没有被@Subscribe注解的public方法(这里插一句,很多时候,如果打正式包的时候EventBus没有做混淆处理,就会抛出该异常,因为方法名被混淆处理了,EventBus会找不到),把获取到的方法集合存入到缓存中去,并且把方法集合return出去。

大致的流程就是这样,findSubscriberMethods()负责获取注册对象的方法集合,优先从缓存中获取,缓存中不存在,则根据ignoreGeneratedIndex是true或false,如果ignoreGeneratedIndex为true,则调用findUsingReflection(),如果为false,则调用findUsingInfo()方法去获取方法集合

findUsingInfo():

private List findUsingInfo(Class> subscriberClass) {

// 这里采用了享元设计模式

FindState findState = prepareFindState();

// 初始化FindState里的参数

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 {

findUsingReflectionInSingleClass(findState);

}

findState.moveToSuperclass();

}

return getMethodsAndRelease(findState);

}

// 获取FindState对象采用了享元模式

private FindState prepareFindState() {

synchronized (FIND_STATE_POOL) {

for (int i = 0; i < POOL_SIZE; i++) {

FindState state = FIND_STATE_POOL[i];

if (state != null) {

FIND_STATE_POOL[i] = null;

return state;

}

}

}

return new FindState();

}

// FindState类定义的信息

static class FindState {

final List subscriberMethods = new ArrayList<>();

final Map anyMethodByEventType = new HashMap<>();

final Map subscriberClassByMethodKey = new HashMap<>();

final StringBuilder methodKeyBuilder = new StringBuilder(128);

Class> subscriberClass;

Class> clazz;

boolean skipSuperClasses;

SubscriberInfo subscriberInfo;

void initForSubscriber(Class> subscriberClass) {

this.subscriberClass = clazz = subscriberClass;

skipSuperClasses = false;

subscriberInfo = null;

}

....

....

}

获取FindState采用了享元模式,这里不多说。之后调用了 findState.initForSubscriber(subscriberClass)方法,只是为了给FindState对象里的一些信息进行赋值操作。再然后是一个 while循环,循环里首先调用getSubscriberInfo(findState)方法,点进去看一下,发现该方法返回null。这里留一个小疑问,getSubscriberInfo()方法什么时候不为null?? 答案在最后一节的高级用法里

private SubscriberInfo getSubscriberInfo(FindState findState) {

// findState.subscriberInfo在初始化的时候置为null,所以该if 分支不会执行

if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {

SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();

if (findState.clazz == superclassInfo.getSubscriberClass()) {

return superclassInfo;

}

}

// subscriberInfoIndexes初始化的时候也是null,并没有赋值

if (subscriberInfoIndexes != null) {

for (SubscriberInfoIndex index : subscriberInfoIndexes) {

SubscriberInfo info = index.getSubscriberInfo(findState.clazz);

if (info != null) {

return info;

}

}

}

return null;

}

所以findUsingInfo()的while循环里直接走了else分支:findUsingReflectionInSingleClass(findState)。

也就是说findUsingInfo()方法的主要逻辑是由findUsingReflectionInSingleClass()方法去完成的(默认情况,不考虑使用apt)

这里有个细节要看一下:

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 {

findUsingReflectionInSingleClass(findState);

}

findState.moveToSuperclass();

}

这个while循环什么时候结束呢?这是我们第一次看EventBus源码看到这里比较疑惑的地方,答案就在这个 findState.moveToSuperclass()里面。

void moveToSuperclass() {

if (skipSuperClasses) {

clazz = null;

} else {

clazz = clazz.getSuperclass();

String clazzName = clazz.getName();

/** Skip system classes, this just degrades performance. */

if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {

clazz = null;

}

}

}

我们可以看到去把clazz赋值为它的超类,直到没有父类为止,才返回clazz=null,循环也才终止。也就是说 这个while循环保证了,获取注解的方法不仅会从当前注册对象里去找,也会去从他的父类查找。

好了,继续分析findUsingReflectionInSingleClass(findState)方法。

private void findUsingReflectionInSingleClass(FindState findState) {

Method[] methods;

try {

// 查找注册对象的所有方法,注意是所有

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) {

// 获取该方法的修饰符,即public、private等

int modifiers = method.getModifiers();

// 修饰符是public才会走该分支

if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {

// 这里是回去该方法的参数类型,String,in

Class>[] parameterTypes = method.getParameterTypes();

// 只有一个参数会走该分支

if (parameterTypes.length == 1) {

// 如果该方法被@subscribe注解会走该分支

Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);

if (subscribeAnnotation != null) {

// 获取传入的对象的Class

Class> eventType = parameterTypes[0];

if (findState.checkAdd(method, eventType)) {

// 获取注解上指定的 线程模型

ThreadMode threadMode = subscribeAnnotation.threadMode();

// 往集合中添加数据

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");

}

}

}

findUsingReflectionInSingleClass()方法首先通过反射去拿到当前注册对象的所有的方法,然后去进行遍历,并进行第一次过滤,只针对修饰符是Public的方法,之后进行了第二次过滤,判断了方法的参数的个数是不是只有一个,如果满足,才去进一步的获取被@subscribe注解的方法。

然后调用

findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()))这行代码,new了一个SubscriberMethod()对象,传入参数,并添加到 findState.subscriberMethods的集合中去.

static class FindState {

final List subscriberMethods = new ArrayList<>();

}

之后,findUsingInfo()的getMethodsAndRelease(findState)方法回去获取刚刚设置的findState的subscriberMethods集合,并把它return出去。代码如下:

private List getMethodsAndRelease(FindState findState) {

// 对subscriberMethods进行了赋值,return出去

List 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;

}

步骤1总结:至此,以上就是EventBus获取一个注册对象的所有的被@subscribe注解的方法的集合的一个过程。该过程的主要方法流程为:

(1) subscriberMethodFinder.findSubscriberMethods()

(2) findUsingInfo()

(3) findUsingReflectionInSingleClass()

步骤2

for (SubscriberMethod subscriberMethod : subscriberMethods) {

subscribe(subscriber, subscriberMethod);

}

通过步骤1,我们已经拿到了注册对象的所有的被@subscribe注解的方法的集合的。现在我们看看subscribe()都做了哪些 操作。

我们不妨想想,如果我们要去做subscribe()时,我们要考虑哪些问题,第一个问题是,要判断一下这些方法是不是已经注册过该事件了要不要考虑方法名是不是相同的问题。第二个问题是一个注册对象中有多个方法注册了该事件,我们该怎么保存这些方法,比如说事件类型是String,一个Activity里有两个方法注册了该事件,分别是onEvent1和onEvent2,那我是不是应该用一个Map集合,以事件类型为key,把onEvent1和onEvent2放到一个List集合中,把该List集合作为value。

subscribe()方法:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

// 拿到事件event类型,比如是String或者自定义的对象

Class> eventType = subscriberMethod.eventType;

// Subscription将注册对象和subscriberMethod 做为参数传入

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

// subscriptionsByEventType是一个Map集合,key是事件类型,验证了我上面的猜想

CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);

// 如果subscriptions是null,则new出一个CopyOnWriteArrayList,并且往Map集合中添加

if (subscriptions == null) {

subscriptions = new CopyOnWriteArrayList<>();

subscriptionsByEventType.put(eventType, subscriptions);

} else {

// 这里做了if语句判断,判断一下List集合中是否存在,存在就抛异常

// 如果不存在?怎么没有add操作? 保持疑问

if (subscriptions.contains(newSubscription)) {

throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "

+ eventType);

}

}

以上的操作验证了我之前的猜想,通过if (subscriptions.contains(newSubscription)) 这个if语句判断 是否发生了重复注册,注意这里重复注册的含义是 事件类型一致,以及方法名也一致。

接下来我们看看如果一个注册对象重复注册了事件Event(方法名不能一致),优先级priority是如何设置的

int size = subscriptions.size();

for (int i = 0; i <= size; i++) {

// 这里判断subscriberMethod的优先级是否是大于集合中的subscriberMethod的优先级,如果是,把newSubscription插进去

// 这也表明了subscription中priority大的在前,这样在事件分发时就会先获取。

if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {

subscriptions.add(i, newSubscription);

break;

}

}

if语句的条件subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) ,保证了subscription中priority大的在前。同时i == size 这个条件也保证了priority小的也会添加到subscriptions集合中去

紧接着我们看看EventBus是如何处理粘性事件的

List> subscribedEvents = typesBySubscriber.get(subscriber);

if (subscribedEvents == null) {

subscribedEvents = new ArrayList<>();

typesBySubscriber.put(subscriber, subscribedEvents);

}

subscribedEvents.add(eventType);

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).

Set, Object>> entries = stickyEvents.entrySet();

for (Map.Entry, 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);

}

}

}

注意以上代码有四行比较重要的注释信息。大致的意思是必须考虑eventType所有子类的现有粘性事件,在迭代的过程中,所有的event可能会因为大量的sticky events变得低效,为了使得查询变得高效应该改变数据结构。

isAssignableFrom方法的意思是判断candidateEventType是不是eventType的子类或者子接口,如果postSticky()的参数是子Event,那么@Subscribe注解方法中的参数是父Event也可以接收到此消息。

拿到粘性Event后,调用了checkPostStickyEventToSubscription()方法,改方法内部方法内部调用了postToSubscription()

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {

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());

}

}

步骤2总结:至此,EventBus的注册操作已经全部分析完了,需要注意的是,粘性事件是在subscribe中进行post的

(二) 发送事件:EventBus.getDefault().post(xxx);

普通Event

public void post(Object event) {

PostingThreadState postingState = currentPostingThreadState.get();

List eventQueue = postingState.eventQueue;

// 将Event添加到List集合中去

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 {

// 遍历这个list集合,条件是集合是否是空的

while (!eventQueue.isEmpty()) {

postSingleEvent(eventQueue.remove(0), postingState);

}

} finally {

postingState.isPosting = false;

postingState.isMainThread = false;

}

}

}

首先将当前的 Event添加到eventQueue中去,并且while循环,处理post每一个Event事件,调用的是 postSingleEvent(eventQueue.remove(0), postingState);

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {

// 获取Event的Class对象

Class> eventClass = event.getClass();

boolean subscriptionFound = false;

// eventInheritance初始化的时候值为true,所以会走该分支

if (eventInheritance) {

// 获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。

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);

}

...

...

}

这里lookupAllEventTypes()方法也是为了获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。

之后是 for循环获取到的Class对象集合,调用postSingleEventForEventType()方法。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {

CopyOnWriteArrayList subscriptions;

synchronized (this) {

// subscriptionsByEventType该map是在subscribe()方法中进行了put操作

subscriptions = subscriptionsByEventType.get(eventClass);

}

if (subscriptions != null && !subscriptions.isEmpty()) {

for (Subscription subscription : subscriptions) {

postingState.event = event;

postingState.subscription = subscription;

boolean aborted = false;

try {

// 进行for循环并调用了postToSubscription()

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;

}

postSingleEventForEventType()方法,主要是获取Event的Class对象所对应的一个List集合,集合的对象是Subscription参数。subscriptionsByEventType对象是在subscribe()方法中进行了赋值。

for循环CopyOnWriteArrayList集合,并调用postToSubscription();

线程模型

等执行到postToSubscription()方法时,线程模型才派上了用场。

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 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);

}

第一个分支:线程模型是POST,直接调用了invokeSubscriber()方法。

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);

}

}

很明显的看到,这是基于反射去调用方法,invoke方法接收两个参数,第一个参数是注册的对象,第二个参数是事件的Event。

从这里就可以看出来,POST并没有去做线程的调度什么的,事件处理函数的线程跟发布事件的线程在同一个线程。

第二个分支:线程模型是MAIN 首先判断了下事件发布的线程是不是主线程,如果是,执行invokeSubscriber()方法,invokeSubscriber()上面已经分析过,如果不是主线程,执行mainThreadPoster.enqueue(subscription, event)。

mainThreadPoster是继承自Handler,从这里大概可以猜到,这一步是去做线程调度的。

我们看一看mainThreadPoster的enqueue做了什么事:

void enqueue(Subscription subscription, Object event) {

// 封装了一个PendIngPost

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

synchronized (this) {

// 将PendIngPost压入队列

queue.enqueue(pendingPost);

if (!handlerActive) {

handlerActive = true;

// 调用了sendMessage()

if (!sendMessage(obtainMessage())) {

throw new EventBusException("Could not send handler message");

}

}

}

}

enqueue() 主要封装了一个PendingPost类,并把subscription和event作为参数传进去,紧接着把PendingPost压入到队列中去,然后发了一条消息sendMessage。

熟悉Handler机制的同学知道,处理消息是在handleMessage()方法中完成的:

public void handleMessage(Message msg) {

boolean rescheduled = false;

try {

long started = SystemClock.uptimeMillis();

while (true) {

PendingPost pendingPost = queue.poll();

if (pendingPost == null) {

synchronized (this) {

// Check again, this time in synchronized

pendingPost = queue.poll();

if (pendingPost == null) {

handlerActive = false;

return;

}

}

}

eventBus.invokeSubscriber(pendingPost);

long timeInMethod = SystemClock.uptimeMillis() - started;

if (timeInMethod >= maxMillisInsideHandleMessage) {

if (!sendMessage(obtainMessage())) {

throw new EventBusException("Could not send handler message");

}

rescheduled = true;

return;

}

}

} finally {

handlerActive = rescheduled;

}

}

代码有点多,我们主要看一下,它接收到消息后,是做了什么处理。从队列中取了消息,并且调用了eventBus.invokeSubscriber(pendingPost)方法,回到EventBus类中。

void invokeSubscriber(PendingPost pendingPost) {

Object event = pendingPost.event;

Subscription subscription = pendingPost.subscription;

PendingPost.releasePendingPost(pendingPost);

if (subscription.active) {

invokeSubscriber(subscription, event);

}

}

该方法内部还是去调用了invokeSubscriber()方法。

分析完线程模型为MAIN 的工作流程,不难做出结论,当发布事件所在的线程是在主线程时,我们不需要做线程调度,直接调用反射方法去执行。如果发布事件所在的线程不是在主线程,需要使用Handler做线程的调度,并最终调用反射方法去执行

第三个分支:线程模型是BACKGROUND。如果事件发布的线程是在主线程,执行

backgroundPoster.enqueue(subscription, event),否则执行invokeSubscriber()。

backgroundPoster实现了Runable接口:

public void enqueue(Subscription subscription, Object event) {

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

synchronized (this) {

queue.enqueue(pendingPost);

if (!executorRunning) {

executorRunning = true;

eventBus.getExecutorService().execute(this);

}

}

}

将PendingPost对象压入队列,然后调用eventBus.getExecutorService().execute(this),交给线程池去进行处理,它的处理是在Runnable的run()中。

@Override

public void run() {

try {

try {

while (true) {

PendingPost pendingPost = queue.poll(1000);

if (pendingPost == null) {

synchronized (this) {

// Check again, this time in synchronized

pendingPost = queue.poll();

if (pendingPost == null) {

executorRunning = false;

return;

}

}

}

eventBus.invokeSubscriber(pendingPost);

}

} catch (InterruptedException e) {

Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);

}

} finally {

executorRunning = false;

}

}

最重要的还是eventBus.invokeSubscriber(pendingPost)这行代码,上面已经分析过。

第四个分支:线程模型是ASYNC。直接调用 asyncPoster.enqueue(subscription, event),asyncPoster也是实现了Runnable接口,里面也是使用的线程池,具体的操作就不分析了,感兴趣的可以去看一下源码,跟上一步操作类似。

(三) 高级用法

EventBus3.0较之前的版本有了一次改造,在3.0之后增加了注解处理器,在程序的编译时候,就可以根据注解生成相对应的代码,相对于之前的直接通过运行时反射,大大提高了程序的运行效率,但是在3.0默认的还是通过反射去查找用@Subscribe标注的方法,一般在使用的时候基本都是这个模式。 那我们怎么配置让EventBus使用注解器生成的代码呢?

在这里我们重点提一下 EventBusBuilder类的:

boolean ignoreGeneratedIndex;

List subscriberInfoIndexes;

subscriberInfoIndexes变量可以去使用注解处理器生成的代码。SubscriberInfoIndex 就是一个接口,而注解生成器生成的类也是继承的它,我们也可以自己去继承它,定制自己的需求,不需要反射的EventBus。

我们再回过头来看一下注册过程的findUsingInfo()方法:

private List findUsingInfo(Class> subscriberClass) {

FindState findState = prepareFindState();

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 {

findUsingReflectionInSingleClass(findState);

}

findState.moveToSuperclass();

}

return getMethodsAndRelease(findState);

}

我们在前面分析的时候,直接分析的 findUsingReflectionInSingleClass(findState)方法,因为getSubscriberInfo()返回null,那什么时候getSubscriberInfo()返回不为null呢 ? 我们具体看看getSubscriberInfo()。

private SubscriberInfo getSubscriberInfo(FindState findState) {

if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {

SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();

if (findState.clazz == superclassInfo.getSubscriberClass()) {

return superclassInfo;

}

}

// 判断subscriberInfoIndexes 是否为null,默认为null,当我们使用apt插件构建代码 的时候,可以手动的调用EventBusBuilder的addIndex,将subscriberInfoIndexes 进行赋值。

if (subscriberInfoIndexes != null) {

for (SubscriberInfoIndex index : subscriberInfoIndexes) {

SubscriberInfo info = index.getSubscriberInfo(findState.clazz);

if (info != null) {

return info;

}

}

}

return null;

}

当我们使用apt插件构建代码 的时候,可以手动的调用EventBusBuilder的addIndex(),将subscriberInfoIndexes 进行赋值。这样subscriberInfoIndexes 就不会为null,getSubscriberInfo()方法也就不会为null。findUsingInfo()也就不会调用反射去获取数据,从而提高了性能。

如何使用新特性SubscriberIndex:

在gradle文件做以下配置:

android {

defaultConfig {

javaCompileOptions {

annotationProcessorOptions {

arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]

}

}

}

}

dependencies {

compile 'org.greenrobot:eventbus:3.1.1'

annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'

}

成功构建项目后,将为您生成使用eventBusIndex指定的类 。然后在设置EventBus时将其传递给它:

EventBus.builder().ignoreGeneratedIndex(false).addIndex(new MyEventBusIndex()).installDefaultEventBus();

我们回过头看,注解器还是很有用的,避免使用反射去消耗了性能。

总结:无论是EventBus还是ButterKnife,都使用到了注解处理器,这方面的知识还是要多去学习。还有的是对一个框架不仅仅要知道怎么调用它的api,还要尽可能的从源码的角度去分析它的实现原理,阅读源码也是一个自我提升的一个过程!

Android的技术讨论Q群:947460837

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/289375.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android之library class android.webkit.WebViewClient depends on program class android.net.http.SslErro

1 问题 编译chromium的源代码的时候,在主类的actitivity里面插入了加载webview的代码 编译错误提示如下 library class android.webkit.WebViewClient depends on program class android.net.http.SslError 2 原因 日志看见有SslError,猜测应该是混淆导致,因为我们加的是web…

如何实现一个线程安全的 ConcurrentHashSet ?

咨询区 Sebastian在 .NET 框架中并没有线程安全的 ConcurrentHashSet 类&#xff0c;我想模仿 ConcurrentDictionary 来实现一个&#xff0c;目前写了一下桩代码。public class ConcurrentHashSet<TElement> : ISet<TElement> {private readonly ConcurrentDiction…

长江存储年底提供自研32层堆叠3D NAND闪存样品

据业内消息称&#xff0c;紫光集团旗下的长江存储技术公司&#xff08;YMTC&#xff09;正在规划开发自己的DRAM内存制造技术&#xff0c;而且可能直奔当今世界最先进的20&#xff0f;18nm工艺。 长江存储技术公司是紫光集团收购武汉新芯部分股权后更名而来的&#xff0c;并邀请…

地图投影系列介绍(四)----投影坐标系

4.4 我国常用地图投影  我国基本比例尺地形图(1:100万、1:50万、1:25万、1:10万、1:5万、1:2.5万、1:1万、1:5000)除1:100万以外均采用高斯-克吕格Gauss-Kruger投影(横轴等角切圆柱投影,又叫横轴墨卡托Transverse Mercator投影)为地理基础。 …

大学物理实验计算机仿真 光电效应,大学物理实验

前言第一章 大学物理实验概论第一节 绪论一、大学物理实验的地位和任务二、大学物理实验课的基本要求三、大学物理实验课的基本程序四、大学物理实验课规则第二节 物理量的测量一、测量与单位二、有效数字三、有效数字的运算规则四、测量的分类第三节 实验误差与不确定度一、实…

char添加一个字符_给你五十行代码把图片变成字符画!程序:太多了,一半都用不完...

今天和大家分享一个简单又好玩的Python项目–“图片转字符画”。废话不多说&#xff0c;先上一个效果图迷惑一下众生。没错的&#xff0c;图片转字符画就是将我们平常所看到的的图片根据像素RGB值和灰度值传化成一个个字符串的过程。嗯…听起来有些费脑子喔。没关系&#xff0c…

Android之You need to use a Theme.AppCompat theme (or descendant) with this activity.

1 问题 点击activity的时候,崩溃日志如下 You need to use a Theme.AppCompat theme (or descendant) with this activity. 2 原因 我类的activity是继承了有主题的activity,但是我在AndroidManifest.xml文件里面声明这个类的时候设置的主题如下 android:theme"android…

oom 如何避免 高并发_微博短视频百万级高可用、高并发架构如何设计?

本文从设计及服务可用性方面&#xff0c;详细解析了微博短视频高可用、高并发架构设计中的问题与解决方案。今天与大家分享的是微博短视频业务的高并发架构&#xff0c;具体内容分为如下三个方面&#xff1a;团队介绍微博视频业务场景“微博故事”业务场景架构设计团队介绍我们…

免费都不管用,Win10升级在中国怎么就这么难?

天下没有免费的午餐&#xff0c;如果有&#xff0c;那也是限时免费。不过&#xff0c;被地摊光盘浸淫十多年的中国民众&#xff0c;这次却对微软的免费分外冷淡。 就在7月26日这天&#xff0c;距离Windows 10免费升级还剩下最后三天&#xff0c;微软发了一份怨念深重的声明——…

IDL中生成等差/等比数列的方法

等差数列: 生成差值为5,

计算机病毒是指______.,计算机病毒是指

语音内容&#xff1a;大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。计算机病毒是编制者在计算机程序中插入的破坏计算机功能或者数据的代码&#xff0c;能影响计算机使用&#xff0c;能自我复制的一组计算机指令或者程序代码。电…

在非k8s 环境下 的应用 使用 Dapr Sidekick for .NET

在k8s 环境下&#xff0c;通过Operator 可以管理Dapr sidecar&#xff0c; 在虚拟机环境下&#xff0c;我们也是非常需要这样的一个管理组件&#xff0c;之前写的一篇文章《 在 k8s 以外的分布式环境中使用 Dapr》 里面介绍了一个案例Dapr case study: Man Group&#xff1a;ht…

sigmoid函数_常用的激活(激励)函数——深度学习笔记(建议收藏)

激活函数(又叫激励函数&#xff0c;后面就全部统称为激活函数)是模型整个结构中的非线性扭曲力&#xff0c;神经网络的每层都会有一个激活函数。那他到底是什么&#xff0c;有什么作用&#xff1f;都有哪些常见的激活函数呢&#xff1f;深度学习的基本原理就是基于人工神经网络…

LINUX - /etc/init.d/nginx: line 51: kill: (29833) - No such process

rootgosstech-All-Series:/home# service nginx stopStopping nginx daemon: /etc/init.d/nginx: line 51: kill: (29833) - No such process 以上是报错&#xff1b; 没有进程了&#xff1b;多半是关机不当&#xff1b;比如断电等&#xff1b; 解觉方法&#xff1a;cd /usr/lo…

Android之屏幕旋转之后当前activity被finish了依然被拉起来

1 问题 屏幕旋转之后当前activity被finish了依然被拉起来 2 分析思路 不用finish函数,使用 android.os.Process.killProcess(android.os.Process.myPid()); 还是被拉起来了,然后采用 System.exit(0); 还是被拉起来了,然后我就看app的进程,有2个进程,当一个activity被finish…

IDL与C#混合编程技术

C# (C Sharp)是微软公司在2000年6月发布的一种新的编程语言。C#与Java有很多的相似

0330Cache Buffers chains与共享模式疑问

[20150330]Cache Buffers chains与共享模式疑问.txt --昨天我看了链接http://blog.itpub.net/22034023/viewspace-708296/,重复测试,无法再现,我修改一下测试方法: 1.建立测试环境: SCOTTtest> ver1 PORT_STRING VERSION BANNER ----------------…

计算机导论上机模拟,计算机导论模拟考试题6份完整版.doc

计算机技术导论模拟考试题第三次模拟考试题1判断题(5). 接口是外部设备和CPU之间的信息中转站。(7). 嵌入式系统CPU大部分采用冯.诺依曼体系结构。(10). Excel工作表的名称由文件名决定。(11). 一个关系只能有一个主键。(12). 关系中元组的次序是任意的。(13). 在关系中能唯一区…

java如何进行字符串拼接?

将字符串数组中的字符串拼接成一个字符串&#xff0c;中间以“,”分割&#xff1b; example&#xff1a; String[] str{"John","lily,","lily","lucy"}; String objectStr""; for(int i0;i<str.length;i){if(i!str.lengt…

江苏信息考试access_软考 信息处理技术员备考复习攻略

如何备考信息技术处理员&#xff1f;在上午题和下午题中哪些知识点是重点难点&#xff1f;以下整理了信息处理技术员相关信息&#xff0c;祝所有考生们都能顺利通过考试。1上午考试试题分析上午试题含盖了纲中的信息技术基本概念、信息处理技术知识、计算机系统基础知识、操作系…