欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

EventBus源码学习,android输入法开发

时间:2023-08-05

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;

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 对这些信息做了一个封装。这些信息在我们处理事件的时候都会用到。

好的,知道了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()方法去获取方法集合

看完这里,我们知道了一个需要注意的地方就是:调用了EventBus.getDefault().register(this)的类一定要添加@Subscribe注解的订阅方法,否则app会崩溃!

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

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 {

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

// This is faster than getMethods, especially when subscribers are fat classes like Activities

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;

}

}

最后

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

本文在开源项目:【GitHub 】中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
);

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;

}

}

最后

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-cj5TIkS1-1644037956741)]

本文在开源项目:【GitHub 】中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。