当前位置:网站首页>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;
}
}
}
边栏推荐
- fs. Path module
- 什么是防火墙?防火墙基础知识讲解
- notepad++
- Use arm neon operation to improve memory copy speed
- 驾驶证体检医院(114---2 挂对应的医院司机体检)
- Introduction Guide to stereo vision (2): key matrix (essential matrix, basic matrix, homography matrix)
- Mengxin summary of LCs (longest identical subsequence) topics
- Can't find the activitymainbinding class? The pit I stepped on when I just learned databinding
- AUTOSAR from getting started to mastering 100 lectures (103) -dbc file format and creation details
- 2311. 小于等于 K 的最长二进制子序列
猜你喜欢
2020 "Lenovo Cup" National College programming online Invitational Competition and the third Shanghai University of technology programming competition
Wechat H5 official account to get openid climbing account
Applet customization component
Hi Fun Summer, play SQL planner with starrocks!
nodejs_ 01_ fs. readFile
Introduction Guide to stereo vision (3): Zhang calibration method of camera calibration [ultra detailed and worthy of collection]
OpenGL - Model Loading
Confusing basic concepts member variables local variables global variables
OpenGL - Coordinate Systems
Applet (subcontracting)
随机推荐
Solution to the problem of the 10th Programming Competition (synchronized competition) of Harbin University of technology "Colin Minglun Cup"
np. allclose
C#绘制带控制点的Bezier曲线,用于点阵图像及矢量图形
Meta tag details
Use arm neon operation to improve memory copy speed
Golang foundation - the time data inserted by golang into MySQL is inconsistent with the local time
nodejs_ fs. writeFile
Introduction Guide to stereo vision (4): DLT direct linear transformation of camera calibration [recommended collection]
c语言指针深入理解
Rebuild my 3D world [open source] [serialization-3] [comparison between colmap and openmvg]
Causes and appropriate analysis of possible errors in seq2seq code of "hands on learning in depth"
fs. Path module
Talking about label smoothing technology
[code practice] [stereo matching series] Classic ad census: (6) multi step parallax optimization
NIPS2021 | 超越GraphCL,GNN+对比学习的节点分类新SOTA
阿里云发送短信验证码
OpenGL - Lighting
C # draw Bezier curve with control points for lattice images and vector graphics
Multiple linear regression (gradient descent method)
交通运输部、教育部:广泛开展水上交通安全宣传和防溺水安全提醒