当前位置:网站首页>Interpretation of eventbus source code
Interpretation of eventbus source code
2022-06-23 17:39:00 【Pen head】
EventBus It's an open source library , It uses publishing / The subscriber pattern is used to decouple the project , Achieve the communication effect between multiple components . The general process is Registered subscribers -> producer POST event -> Subscriber consumption
One 、Register
There are mainly 2 A note ,findSubscriberMethods Function and subscribe function
public void register(Object subscriber) {
if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) {
// Crash if the user (developer) has not imported the Android compatibility library.
throw new RuntimeException("It looks like you are using EventBus on Android, " +
"make sure to add the \"eventbus\" Android library to your dependencies.");
}
//-------------------------------------------------------1
Class<?> subscriberClass = subscriber.getClass();
//-------------------------------------------------------2
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//------------------------------------------------3
subscribe(subscriber, subscriberMethod);
}
}
}1. Get the current subscriber
2. Get the current class that is @Subscriber The method of annotation is Subscription method , Details please see findSubscriberMethods()
3. Traverse all subscription methods , The subscription event is stored in subscriptionsByEventType、 The subscription type is stored in typesBySubscriber in , View details subscribe()
One 、findSubscriberMethods
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//------------------------------------------------------1
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//---------------------------------------------------------2
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//--------------------------------------------------------3
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//--------------------------------------------------------4
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}1. Get subscription method from cache
2.ignoreGeneratedIndex Default false, If true, Query subscription method through reflection , For details, see findUsingReflectionInSingleClass()
4. Add the subscription method to METHOD_CACHE In cache .
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// ----------------------------------------------1
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
try {
methods = findState.clazz.getMethods();
} catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
String msg = "Could not inspect methods of " + findState.clazz.getName();
if (ignoreGeneratedIndex) {
msg += ". Please consider using EventBus annotation processor to avoid reflection.";
} else {
msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
}
throw new EventBusException(msg, error);
}
findState.skipSuperClasses = true;
}
//---------------------------------------------------2
for (Method method : methods) {
//---------------------------------------------------3
int modifiers = method.getModifiers();
//-----------------------------------------------4
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//-----------------------------------------------5
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//---------------------------------------6
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
//-------------------------------7
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");
}
}
}1. Find all methods for registering objects
3. Get the modifier of the method , namely public、private etc.
4. Just look at the modifier public Methods
5. Return the parameter type of the method
6. View only the @subscribe Method of annotation , Subscription method .
7. Add the subscription method to FindState Collection
Two 、subscribe
above findSubscriberMethods Find all subscription methods , Next, you need to deal with these subscription methods , For example, the subscription method cannot be duplicated 、 According to the type of subscription, it is key Store the subscription events to according to priority subscriptionsByEventType in 、 The subscription type is stored in typesBySubscriber in , Later in unregister Use in .
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//-------------------------------------------------------1
Class<?> eventType = subscriberMethod.eventType;
//-------------------------------------------------------2
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//-------------------------------------------------------3
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//-------------------------------------------------------4
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);
}
}
//--------------------------------------------------------5
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;
}
}
//-------------------------------------------------------6
List<Class<?>> 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<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);
}
}
}1. Get events EventType, For example String Or custom objects
2. Construct a subscription event according to the subscriber and subscription method
3. according to EventType Find all existing subscription Events ,
5. Traverse all of subscriber identical EventType Subscription events , Here we judge the present subscriberMethod Whether the priority of the subscription method is greater than that in the collection subscriberMethod The priority of the , If it is , hold newSubscription Insert in , It also shows that subscription in priority The big one is ahead , In this way, when the event is distributed, it will get .
6. Save all subscription method content types in the current subscriber to typesBySubscriber in , Later in unregister Will use , View details UnRegister
Two 、Post
public void post(Object event) {
//---------------------------------------------------------1
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
//----------------------------------------------------------2
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//------------------------------------------------2
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}1.PostingThreadState Save event queue and thread state information
2. This is used here. ThreadLocal Technical solution , In different threads ,post Don't interfere with each other , If in the same thread , The subscription method is slow ,eventQueue There is always data , Then don't take this step , Just store the subscription directly in the queue . In different threads eventQueue, Do not affect each other .
2. Traverse post Single subscription , View details postSingleEvent()
One 、postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//------------------------------------------------------1
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//----------------------------------------------2
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));
}
}
}1.eventInheritance The default is true, That is, get the current Event Of Class Of all the parent classes of the object Class A collection of objects , Priority is given to reading from the cache , View details lookupAllEventTypes()
2.postSingleEventForEventType View details postSingleEventForEventType Method
3. Can't find subscription Errors will be thrown when subscribing to events No subscribers registered for event perhaps sendNoSubscriberEvent If it is not empty, send NoSubscriberEvent event .
One 、lookupAllEventTypes
Traverse the subscription and its parent class , Store in eventTypesCache in .
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}Two 、postSingleEventForEventType
Traverse subscription Events , Store subscription events in threadLocal in , At the same time, distribute the event
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
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;
}private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//---------------------------------------------1
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
//----------------------------------------2
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
//-----------------------------------------3
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);
}
}1. Take out the thread mode of the subscription method , After that, we can process... According to the thread mode
2. Internally, reflection calls are used directly ,poster.enqueue The internal is also a combination of reflection calls handler Handle
3、 ... and 、UnRegister
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
// Yes subscriptionsByEventType Removed the subscriber All subscription information for
unsubscribeByEventType(subscriber, eventType);
}
// Removed the registered object and all its corresponding Event Event list
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> 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--;
}
}
}
}边栏推荐
- Self supervised learning (SSL)
- 【30. 串联所有单词的子串】
- Installation, configuration, désinstallation de MySQL
- Intel arc A380 graphics card message summary: the entry-level price products of running point and bright driving need to be optimized
- bypassuac提权
- 如何设计一个秒杀系统?
- C. Product 1 Modulo N-Codeforces Round #716 (Div. 2)
- NLP paper reading | improving semantic representation of intention recognition: isotropic regularization method in supervised pre training
- Réponse 02: pourquoi le cercle Smith peut - il "se sentir haut et bas et se tenir à droite et à droite"?
- QT布局管理器【QVBoxLayout,QHBoxLayout,QGridLayout】
猜你喜欢

Can the asemi fast recovery diodes RS1M, us1m and US1G be replaced with each other

Google Play Academy 组队 PK 赛,火热进行中!

What does the timestamp 90K mean?
![QT layout manager [qvboxlayout, qhboxlayout, qgridlayout]](/img/62/a0425a325f123ba91a7a69cf854d2c.png)
QT layout manager [qvboxlayout, qhboxlayout, qgridlayout]

图扑数字孪生 3D 风电场,智慧风电之海上风电

How to use SQL window functions

混沌工程在云原生中间件稳定性治理中的实践分享

Wechat applet: time selector for the estimated arrival date of the hotel

Identify and stop the process that's listening on port 8080 or configure this application

Network remote access raspberry pie (VNC viewer)
随机推荐
MySQL transaction and its characteristics and locking mechanism
mysql-选择使用Repeatable read的原因
解答02:Smith圓為什麼能“上感下容 左串右並”?
EasyPlayer移动端播放webrtc协议时长按播放页面无法关闭“关于我们”页面
官方零基础入门 Jetpack Compose 的中文课程来啦
数据库 实验二 查询
What does the timestamp 90K mean?
图扑软件数字孪生挖掘机实现远程操控
The official Chinese course of zero foundation introduction jetpack compose is coming
Network remote access raspberry pie (VNC viewer)
How to make sales management more efficient?
How do you choose to buy stocks? Good security?
《AN4190应用笔记 天线选择指南》——天线理论2
浅谈5类过零检测电路
MySQL - reasons for using repeatable read
MySQL事务及其特性与锁机制
Get first and last days by year
单火线设计系列文章10:拓展应用-单火开关实现双控
Analytic analog-to-digital (a/d) converter
Read the typical application circuit of microphone