当前位置:网站首页>Reading and understanding of eventbus source code
Reading and understanding of eventbus source code
2022-07-07 14:22:00 【LLAiden】
EventBus
Android Mainstream event delivery Library in , Simplify callbacks and thread operations in our code
EventBus introduce
implementation 'org.greenrobot:eventbus:3.2.0'
register
EventBus.getDefault().register(this);
Next, let's see how to register here
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
The first is a single example of a standard double lock hungry man , Let's look at
regsiter()
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
First of all to see
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> 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 {
// Use in this class Subscribe Annotated functions are added to the cache
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
If there is no cache, it will execute
subscriberMethods = findUsingInfo(subscriberClass);
Let's move on
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// there findState.clazz It was passed in when we registered this
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
// Here for null Executive else Branch
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 {
// Continue to look at the source code here
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
// Get the function in this class in the signature
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) {
// Check whether this function has Subscribe annotation
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
// Get the parameters of this function class It's the event we deliver class
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
// Get what we set threadMode
ThreadMode threadMode = subscribeAnnotation.threadMode();
// Use this class Subscribe Annotated functions are added subscriberMethods in
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
}
}
}
}
It's over findSubscriberMethods()
Let's keep looking subscribe()
What have you done
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
// This object holds our subscription class and subscriber Annotated functions
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
// Put this type of Event Add to this Map
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) {
// This List In the above function, it has been stored in subscriptionsByEventType It's in
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
// Which types of events this class focuses on are stored in typesBySubscriber
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// Each registration will trigger a sticky Event Sending of
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);
}
}
}
Come here register I have finished reading , This is actually getting it for use Subscribe Annotated functions and related information ( Thread mode , priority , Whether it is a sticky event )
Event sending
Let's continue to look at the sending of events and how they are called , Take a look first EventBus.getDefaule().post()
public void post(Object event) {
// First, get the thread status , Because the default threadMode yes ThreadMode.POSTING
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
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()) {
// Send this event , Add this to the front list
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;
// Creating EventBus Whether this of the object is true Of
if (eventInheritance) {
// Here, the event class class as well as Object.class Save in eventTypes
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) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
// Get the... In the cache first
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
// Our event class
eventTypes.add(clazz);
// This function will not execute internally without inheriting the interface
addInterfaces(eventTypes, clazz.getInterfaces());
// The second loop will add the parent of the event class to this list
clazz = clazz.getSuperclass();
}
// Add to cache , Go down and get it directly from the cache , It's better
eventTypesCache.put(eventClass, eventTypes);
}
// Now there are events class,Object.class, Because all classes implicitly inherit Object
return eventTypes;
}
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// stay register Called subscribe The function takes eventClass Events of type are stored in subscriptionsByEventType
// All this here if you register this type Event,subscriptions Not for null
// For details of this data storage, please see subscribe()
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
try {
// Here is the sending of events , Continue to look at the source code of this function
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) {
switch (subscription.subscriberMethod.threadMode) {
//threadMode The default is POSTING
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
//Android Main thread execution
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
//mainThreadPoster When it was created, it was passed into Looper.getMainLooper()
// And use this Looper Created Handler object , So here is the use of Handler The mechanism is executed in the main thread
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
// Execute in the main thread first ,mainThreadPoster==null Execute in the sending thread
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
// Execute... In the process pool
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
// Asynchronous execution , Like and BACKGROUND There's no difference
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
// Here is the function to be executed , Use reflection to execute
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);
}
}
clazz = clazz.getSuperclass();
It's worth noting , That is to say, we write a parameter of type Object Function of , And use Subscribe Annotation, then it can receive all our events
summary
- stay EventBus Instantiation time , Use the standard double lock mechanism to realize the singleton
- In execution
register()
Function EventBus You will find that the modifier under this class is public, The number of parameters is 1, And use Subscribe Annotated functions , stay map For storage register()
The function also sends sticky events , This is why we send sticky events first , Later, you can also receive the reason for the event by subscribing- stay
post()
We will first find the event type in Map The corresponding function in , And use reflection to execute this function - EventBus Use in Handler Thread scheduling
边栏推荐
- AutoCAD - how to input angle dimensions and CAD diameter symbols greater than 180 degrees?
- Redis 核心数据结构 & Redis 6 新特性详
- Million data document access of course design
- Use day JS let time (displayed as minutes, hours, days, months, and so on)
- Cascading update with Oracle trigger
- MRS离线数据分析:通过Flink作业处理OBS数据
- IP and long integer interchange
- Leetcode——236. The nearest common ancestor of binary tree
- OAuth 2.0 + JWT protect API security
- Flask session forged hctf admin
猜你喜欢
The longest ascending subsequence model acwing 1012 Sister cities
GVIM [III] [u vimrc configuration]
Parsing of XML files
gvim【三】【_vimrc配置】
Codes de non - retour à zéro inversés, codes Manchester et codes Manchester différentiels couramment utilisés pour le codage des signaux numériques
How to check the ram and ROM usage of MCU through Keil
Reverse non return to zero code, Manchester code and differential Manchester code of common digital signal coding
Docker deploy Oracle
Horizontal of libsgm_ path_ Interpretation of aggregation program
Equipment failure prediction machine failure early warning mechanical equipment vibration monitoring machine failure early warning CNC vibration wireless monitoring equipment abnormal early warning
随机推荐
Use day JS let time (displayed as minutes, hours, days, months, and so on)
IP address home location query
Excuse me, does PTS have a good plan for database pressure measurement?
CSMA/CD 载波监听多点接入/碰撞检测协议
PERT图(工程网络图)
Huawei image address
Docker deploy Oracle
Similarities and differences between switches and routers
AI talent cultivation new ideas, this live broadcast has what you care about
请问,如图,pyhon云函数提示使用了 pymysql模块,这个是怎么回事?
GAN发明者Ian Goodfellow正式加入DeepMind,任Research Scientist
【网络安全】sql注入语法汇总
SAKT方法部分介绍
Excuse me, as shown in the figure, the python cloud function prompt uses the pymysql module. What's the matter?
FC连接数据库,一定要使用自定义域名才能在外面访问吗?
Seven propagation behaviors of transactions
Cesium 已知一点经纬度和距离求另一个点的经纬度
c#利用 TCP 协议建立连接
VSCode 配置使用 PyLint 语法检查器
ES日志报错赏析-Limit of total fields