当前位置:网站首页>Analysis of eventbus source code
Analysis of eventbus source code
2022-07-05 09:15:00 【Xu Jiajia 233】
summary
This article is suitable for EventBus have interest in , Or have been right EventBus Readers with certain use experience .
If the reader has not used it before EventBus, It is recommended to read the author's previous article :
register
Key logic :
Traverse the currently registered class , Get which used eventBus Method of annotation .
Register these methods to two HashMap in , Namely subscriptionsByEventType and typesBySubscriber. adopt synchronized Lock , To ensure thread safety .
subscriptionsByEventType:key yes eventType,value yes List
typesBySubscriber:key Is a registered object ,value yes ListMethod execution , Will judge whether there is stcky event , If any, it will trigger directly .
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> 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();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
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);
}
}
}
post
Key logic :
- post The source code does two things , The first thing is to change the current event Add to eventQueue in . The second thing is to change the current thread to posting state .
- posting The status will be processed circularly eventQueue Medium event, Put it in the corresponding subscription In the implementation of .
– lookup subscription The process is : First find and event Related to the class , Then traverse these classes and their related subscription.
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
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 {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> 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) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, 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 = 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;
}
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 = 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;
}
postSticky
Key logic :
- And post comparison ,postSticky Will be will be event Add to stickyEvents This Map in .
- in front register The logic in has been mentioned , When a class is registered , Will judge whether there is stickyEvent, If any, it will trigger directly .
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
Multithreaded logic
Key logic :
- Finally trigger subscription when , Will be in postToSubscription Select the thread to execute .
- POSTING: Trigger directly on the current thread
- MAIN: If the current thread is the main thread , Then trigger directly . If you are not currently in the main thread , Then it will pass handler Throw it to the main thread to execute .
- BACKGROUND: If it is currently a child thread , Then trigger directly . If you are not currently in a child thread , Then it will be thrown to eventBus In the thread pool .
- ASYNC: Throw to eventBus In the thread pool .
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);
}
}
final class BackgroundPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
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);
}
}
}
@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;
}
}
}
边栏推荐
- Golang foundation -- map, array and slice store different types of data
- 交通运输部、教育部:广泛开展水上交通安全宣传和防溺水安全提醒
- Svgo v3.9.0+
- Hi Fun Summer, play SQL planner with starrocks!
- Oracle advanced (III) detailed explanation of data dictionary
- Pearson correlation coefficient
- L'information et l'entropie, tout ce que vous voulez savoir est ici.
- Generate confrontation network
- Introduction Guide to stereo vision (2): key matrix (essential matrix, basic matrix, homography matrix)
- Editor use of VI and VIM
猜你喜欢
[code practice] [stereo matching series] Classic ad census: (6) multi step parallax optimization
Ros- learn basic knowledge of 0 ROS - nodes, running ROS nodes, topics, services, etc
嗨 FUN 一夏,与 StarRocks 一起玩转 SQL Planner!
fs. Path module
nodejs_ 01_ fs. readFile
优先级队列(堆)
Nodemon installation and use
Introduction Guide to stereo vision (6): level constraints and polar correction of fusiello method
Shutter uses overlay to realize global pop-up
Hi Fun Summer, play SQL planner with starrocks!
随机推荐
Transfer learning and domain adaptation
利用请求头开发多端应用
OpenGL - Lighting
My experience from technology to product manager
520 diamond Championship 7-4 7-7 solution
scipy.misc.imread()
Mengxin summary of LIS (longest ascending subsequence) topics
Ros-11 common visualization tools
12. Dynamic link library, DLL
np.allclose
Multiple linear regression (gradient descent method)
Mengxin summary of LCs (longest identical subsequence) topics
Use arm neon operation to improve memory copy speed
Rebuild my 3D world [open source] [serialization-1]
【愚公系列】2022年7月 Go教学课程 003-IDE的安装和基本使用
交通运输部、教育部:广泛开展水上交通安全宣传和防溺水安全提醒
3D reconstruction open source code summary [keep updated]
Golang foundation - the time data inserted by golang into MySQL is inconsistent with the local time
np. allclose
嗨 FUN 一夏,与 StarRocks 一起玩转 SQL Planner!