看到这里有没有疑惑的地方?既然是单例模式为什么构造方法不是private而是public,这是因为EventBus可能有多条总线,订阅者注册到不同线上的 EventBus,通过不同的实例来发送数据,不同的 EventBus 是相互隔离开的,订阅者都只会收到注册到该线上事件。
构造方法中只有一个DEFAULT_BUILDER,接着我们来看DEFAULT_BUILDER
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
我们可以看出EventBus是通过一个EventBusBuilder的实例来构建的
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;
}
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
前三行是三个hashMap但有所不同:
subscriptionsByEventType 对应的hashMap是:
private final Map<Class<?>, CopyOnWriteArrayList> subscriptionsByEventType;
它的key是订阅事件,value是所有订阅者集合。当我们发送Event事件的时候都是从这个集合中去寻找,
typesBySubscriber 对应的hashMap是:
private final Map<Object, List<Class<?>>> typesBySubscriber;
它的key是订阅者对象,value是这个订阅者订阅的所有事件集合。当我们注册或者反注册的时候都是操作这个集合然后操作subscriptionsByEventType。
stickyEvents 对应的hashMap是:
private final Map<Class<?>, Object> stickyEvents;
它的key是粘性事件的class对象,value是粘性事件对象。接着我们看后面三行代码:
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
mainThreadPoster
我们知道,在此之前我们线程间通信或者传值的话 都是使用Hanlder,而mainThreadPoster 的本质就是一个Hanlder,我们点击mainThreadPoster 进去看
既然是hanlder所以我们主要来看它的hanleMessage方法
@Override
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;
}
}
从队列中不断的去处pendingPost,使用 eventBus.invokeSubscriber(pendingPost)进行事件分发,而pendingPost是一个可复用对象的复用池,通过obtainPendingPost方法进行复用,releasePendingPost方法进行回收,这个HandlerPoster是运行在主线程中的,因为初始化的时候有个 Looper.getMainLooper()。
pendingPost主要有三个对象:事件、订阅和一个节点
backgroundPoster
backgroundPoster本质是一个Runnable,主要在后台处理事件,所以我们主要看它的run方法
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;
}
}
和mainThreadPoster是一样的,从复用池中取出,如果队列为空就间隔1秒再取,然后调用invokeSubscriber方法进行分发
asyncPoster
asyncPoster的本质也是一个Runnable
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException(“No pending post available”);
}
eventBus.invokeSubscriber(pendingPost);
}
但是每次只取一个pendingPost。不论发布线程是否为主线程,都使用一个空闲线程来处理。Async类的所有线程是相互独立的,因此不会出现卡线程的问题。
说到这里这三个poster都是负责线程调度的,最后都调用invokeSubscriber进行事件分发,那么我们有必要来来看下这个invokeSubscriber方法。
invokeSubscriber
/**
-
Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents race conditions
-
between {@link #unregister(Object)} and event delivery. Otherwise the event might be delivered after the
-
subscriber unregistered. This is particularly important for main thread delivery and registrations bound to the
-
live cycle of an Activity or Fragment.
*/
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
invokeSubscriber(subscription, event);
}
}
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);
}
}
这样就比较一目了然了,从peningPost中得到事件和订阅,通过反射调用了订阅者的订阅函数并把event对象作为参数传入。
二、注册事件
======
/**
-
Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
-
are no longer interested in receiving events.
-
-
Subscribers have event handling methods that must be annotated by {@link Subscribe}.
-
The {@link Subscribe} annotation also allows configuration like {@link
-
ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
我们可以看到这个注册方法
首先获取订阅者的class对象,通过subscriberMethodFinder寻找这个class对象的所有订阅方法集合SubscriberMethod,SubscriberMethod,而SubscriberMethod中包含了相应的线程、Method对象、事件类型、优先级记忆是否是粘性事件等。
然后通过 subscribe(subscriber, subscriberMethod);订阅事件,而subscriberMethod对象是通过subscriberMethodFinder获取的,所以我们先来看下subscriberMethodFinder的实现
subscriberMethodFinder
subscriberMethodFinder类是用来查找和缓存订阅者响应函数的信息的类,那么我们如何获取订阅者响应函数的信息,这里就要提到APT运行时注解了,当然我们这里不对APT进行介绍,EventBus中是通过@Subscribe()注解来获取的,我们来看下@Subscribe()
言归正传我们来看subscriberMethodFinder方法
List findSubscriberMethods(Class<?> subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
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;
}
}
从METHOD_CACHE取看是否有缓存,key是保存订阅类的类名,value是保存类中订阅的方法数据,如果忽略注解器生成的MyEventBusIndex类就调用反射来获取订阅类中的订阅方法信息,如果没有忽略就从注解生成的是否忽略注解器生成的MyEventBusIndex类中来获取订阅类中的订阅方法信息。
findUsingReflection
private List findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//通过反射来获得订阅方法信息
findUsingReflectionInSingleClass(findState);
//查找父类的订阅方法
findState.moveToSuperclass();
}
//返回订阅方法集合
return getMethodsAndRelease(findState);
}
findUsingReflectionInSingleClass
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;
}
//遍历Method
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 subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
//校验是否添加该方法
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
//实例化SubscriberMethod对象并添加
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
findUsingInfo是通过运行时注解生成的MyEventBusIndex类来获取订阅方法信息
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);
}
getSubscriberInfo
private SubscriberInfo getSubscriberInfo(FindState findState) {
//判断FindState对象中是否有缓存的订阅方法
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
这样,订阅类的所有SubscriberMethod都已经被保存了,最后再通过getMethodsAndRelease()返回List。
然后我们再回到注册中的subscribe(subscriber, subscriberMethod);方法,方法代码如下:
//必须在同步代码块里调用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取订阅的事件类型
Class<?> eventType = subscriberMethod.eventType;
//创建Subscription对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//从subscriptionsByEventType里检查是否已经添加过该Subscription,如果添加过就抛出异常
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);
}
}
//根据优先级priority来添加Subscription对象
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;
}
}
//将订阅者对象以及订阅的事件保存到typesBySubscriber里.
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/a3bb83f2bc48160c4bf08bb141d6a39c.jpeg)
最后
我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。
不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?
我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。
我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
Hj-1712487040458)]
[外链图片转存中…(img-5Z7ERjMu-1712487040459)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/a3bb83f2bc48160c4bf08bb141d6a39c.jpeg)
最后
我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。
不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?
我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。
我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:
[外链图片转存中…(img-EJXHmAzB-1712487040459)]
[外链图片转存中…(img-yae4N8jg-1712487040459)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!