EventBus believes that many people are familiar with it. Although Google has officially released JetPack to replace it, some design ideas of EventBus are still worth learning. Let’s write a simple EventBus case below
In fact, the principle of EventBus is not difficult, it is to maintain several arrays, then find the corresponding registered object according to the corresponding key, and call the corresponding method by radiation.
There is a big difference before and after EventBus3.0. The biggest difference is that after 3.0, a reference object is generated during compiling through apt, which greatly improves performance.
The easiest to use
//注册事件
EventBus.getDefault().register(this);
//注册方法
@Subscribe
public void event(BaseEventBusBeaan message) {
LogUtils.d("EventBusActivity event");
}
//发送事件
EventBus.getDefault().post(new BaseEventBusBeaan("123", new Bundle()));
//回收
EventBus.getDefault().unregister(this);
post process
First, we should clarify our needs, we need to be in post
when an object out of all the registered listeners that the object class can receive this notification, then this should require an array to store data.
//post出去的对象为key,一个注册者Subscription的list作为value
private Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//这个Subscription包括下面参数
public class Subscription {
final Object subscriber; //activity或者fragment
final SubscriberMethod subscriberMethod;
public Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
}
}
public class SubscriberMethod {
private String methodName; // 订阅方法名
private Method method; // 订阅方法,用于最后的自动执行订阅方法
private ThreadMode threadMode; // 线程模式
private Class<?> eventType; // 事件对象Class,如:UserInfo.class
}
With subscriptionsByEventType
this, we can post()
find all registrants according to the sent events, traverse list
and reflect one by one.
public void post(Object event) {
postSingleEventForEventType(event, event.getClass());
}
//遍历所有的订阅者,发送对应的事件
private void postSingleEventForEventType(Object event, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
invokeSubscriber(subscription, event);
}
}
}
//这里暂时不考虑线程的问题
private void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.getMethod().invoke(subscription.subscriber, event);
} catch (Exception e) {
e.printStackTrace();
}
}
The above is a simplified version of the post
process.
Register process
The above-mentioned difference is post
a very key point, that is subscriptionsByEventType
the source of the data, we should naturally think of it in register
the process.
Look back at subscriptionsByEventType
the key and value again, and find that most of these values can be obtained from the following functions.
@Subscribe
public void event(BaseEventBusBeaan message) {
LogUtils.d("EventBusActivity event");
}
So we need to traverse all the methods in the class, find all the @Subscribe
annotated functions, and save them.
The apt scheme is adopted here . During the compilation process, it traverses all classes, finds all @Subscribe
annotated functions, and saves them in a certain format. The result will generate classes similar to the following.
//具体的生成过程不再这里赘述,想要了解的可以自己看文末的代码
//编译过程中将所有 @Subscribe注释过的方法保存到SUBSCRIBER_INDEX数组中。
//key为函数所属的类,比如MainActivity,value则是一个对象,保存一个数据的集合。
public final class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class,SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(EventBusActivity2.class,
new SubscriberMethod[] {
new SubscriberMethod(EventBusActivity2.class, "event", BaseEventBusBeaan.class, ThreadMode.POSTING, 0, false),
new SubscriberMethod(EventBusActivity2.class, "sticky", UserInfo.class, ThreadMode.POSTING, 2, true),
new SubscriberMethod(EventBusActivity2.class, "sticky2", UserInfo.class, ThreadMode.POSTING, 2, true)}
));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class subscriberClass) {
return SUBSCRIBER_INDEX.get(subscriberClass);
}
}
With MyEventBusIndex
After that, start register
the process.
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = findSubscriberMethods(subscriberClass);
//这个循环是生成subscriptionsByEventType对象的关键,
for (SubscriberMethod method : subscriberMethods) {
subscribe(subscriber, method);
}
}
//1.根据subscriberClass先从methodBySubscriber缓存中找
private List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = methodBySubscriber.get(subscriberClass);
if (subscriberMethods != null) return subscriberMethods;
subscriberMethods = findByAPT(subscriberClass);
if (subscriberMethods != null) {
methodBySubscriber.put(subscriberClass, subscriberMethods);
}
return subscriberMethods;
}
//2.接着从subscriberInfoIndex查找,subscriberInfoIndex这个对象就是上文中提到的MyEventBusIndex的对象
private List<SubscriberMethod> findByAPT(Class<?> subscriberClass) {
if (subscriberInfoIndex == null) {
throw new RuntimeException("未添加索引文件");
}
SubscriberInfo subscriberInfo = subscriberInfoIndex.getSubscriberInfo(subscriberClass);
if (subscriberInfo != null) return Arrays.asList(subscriberInfo.getSubscriberMethods());
return null;
}
Then start traversal subscriberMethods
(because each subscriber does not necessarily have only one method to add @Subscribe
annotations)
for (SubscriberMethod method : subscriberMethods) {
subscribe(subscriber, method);
}
//在这里就可以生成post过程中所需要的 subscriptionsByEventType 数据了。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.getEventType();
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
}
Subscription subscription = new Subscription(subscriber, subscriberMethod);
subscriptions.add(i, subscription);
//订阅者类型集合,unregister的时候用到
List<Class<?>> subscribeEvents = typeBySubscriber.get(subscriber);
if (subscribeEvents == null) {
subscribeEvents = new ArrayList<>();
typeBySubscriber.put(subscriber, subscribeEvents);
}
subscribeEvents.add(eventType);
}
At this point, in fact, a simple process has been passed.
Summarize the general process
- It will all be apt at compile time by
@Subscribe
adding annotation function to theMyEventBusIndex
object. - Data
register
generated in the processsubscriptionsByEventType
. - In
post
through the processsubscriptionsByEventType
to find the corresponding function data, and then reflected by the way call.
Priority issue
This problem is also very simple. You only need to make a priority judgment when inserting data.
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.getEventType();
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
}
Subscription subscription = new Subscription(subscriber, subscriberMethod);
//根据优先级插队
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.getPriority() > subscriptions.get(i).subscriberMethod.getPriority()) {
if (!subscriptions.contains(subscription)) subscriptions.add(i, subscription);
break;
}
}
//订阅者类型集合,unregister的时候用到
List<Class<?>> subscribeEvents = typeBySubscriber.get(subscriber);
if (subscribeEvents == null) {
subscribeEvents = new ArrayList<>();
typeBySubscriber.put(subscriber, subscribeEvents);
}
subscribeEvents.add(eventType);
}
Sticky event
Ordinary events are registered first, then sent. On the contrary, sticky events are sent first and then registered.
We only need to change the order. Store the event when sending, and then register
check if there is a suitable event when
public void postSticky(Object event) {
stickyEvents.put(event.getClass(), event);
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
....
//检查是否有合适的事件可以触发
sticky(subscriberMethod, eventType, subscription);
}
private void sticky(SubscriberMethod subscriberMethod, Class<?> eventType, Subscription subscription) {
if (subscriberMethod.isSticky()) {
Object event = stickyEvents.get(eventType);
if (event != null) {
postToSubscription(subscription, event);
}
}
}
The last postToSubscription
code added.
private void postToSubscription(final Subscription subscription, final Object event) {
switch (subscription.subscriberMethod.getThreadMode()) {
case POSTING: // 订阅、发布在同一线程
invokeSubscriber(subscription, event);
break;
case MAIN:
//事件发送方是主线程
if (Looper.myLooper() == Looper.getMainLooper()) {
invokeSubscriber(subscription, event);
} else {
//事件发送方是子线程
handler.post(new Runnable() {
@Override
public void run() {
invokeSubscriber(subscription, event);
}
});
}
break;
case ASYNC:
//发送方在主线程
if (Looper.myLooper() == Looper.getMainLooper()) {
executorService.execute(new Runnable() {
@Override
public void run() {
invokeSubscriber(subscription, event);
}
});
} else {
invokeSubscriber(subscription, event);
}
break;
}
}
private void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.getMethod().invoke(subscription.subscriber, event);
} catch (Exception e) {
e.printStackTrace();
}
}