记录一下EventBus的使用心得。
简单使用分为5步:
在Activity,Fragment,Service等中
1.创建一个实体类,作为Event实体,如SevenEvent
2.在初始化时注册:EventBus.getDefault().register(this);
3.重写(接收)处理方法,如onEventMainThread(SeventEvent sevent)
4.发送事件:EventBus.getDefault().post(SevenEvent sevent);
5.页面销毁时取消注册:EventBus.getDefault().unregister(this);
其中,事件的发布由post方法实现,事件的订阅(接收)有四个函数可以实现。
onEvent:如果使用这个作为订阅函数,那么该事件在哪个线程发布出来,onEvent就会在哪个线程中运行,也就是说发布事件 和 接收事件在同一个线程。
使用这个方法时,在onEvent方法中不能执行耗时操作,因为容易导致事件分发延迟。
onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,在这里可以更新UI,但是不能执行耗时操作。
onEventBackground:如果使用onEventBackground作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程发布出来的,那么onEventBackground函数直接在这个子线程中执行。
onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程来执行onEventAsync。
那么问题来了,EventBus怎么知道要使用哪个订阅函数?也就是说哪个订阅函数会被调用?
答案是:因为post中传入的参数是自定义类的实例,所以哪个订阅函数传入的参数 与 post中的参数 匹配,就调用哪个订阅函数。如果有两个订阅函数都满足条件,那么这两个订阅函数都会被调用。
备注:
EventBus 2.0中四个订阅函数都是以onEvent开头的
EventBus 3.0中不再要求必须以onEvent开头,而是加上注解来标记,方法名可以随意。如:@Subscribe(threadMode = ThreadMode.MainThread, sticky = true)
public void onMessageEventMainThread(MessageEvent messageEvent){
}
另外,看完EventBus源码之后总结:
在一个单例内部维持着一个map对象存储了一堆的方法;
post就是根据参数去遍历map中的方法,查找到方法,进行反射调用。
部分源码:
单例:(双重判断)
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
register方法:
public void register(Object subscriber) {
register(subscriber, DEFAULT_METHOD_NAME, false, 0);
}
public void register(Object subscriber, int priority) {
register(subscriber, DEFAULT_METHOD_NAME, false, priority);
}
public void registerSticky(Object subscriber) {
register(subscriber, DEFAULT_METHOD_NAME, true, 0);
}
public void registerSticky(Object subscriber, int priority) {
register(subscriber, DEFAULT_METHOD_NAME, true, priority);
}
这四个方法本质上调用了同一个方法:
private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),methodName);
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
subscriber就是传入的this。
methodName是写死的:onEvent,就是扫描以这个名字开头的方法
调用内部类SubscriberMethodFinder的findSubscriberMethods方法,传入了subscriber的class,以及methodName,返回一个List。
也就是遍历该类内部所有的方法,然后根据methodName去匹配,匹配成功的封装成SubscriberMethod,然后返回一个list。
方法subscribe(subscriber, subscriberMethod, sticky, priority){}中:
Class<?> eventType = subscriberMethod.eventType;
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//subscriptionsByEventType是Map,这个Map就是EventBus存储方法的地方。
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);//封装
post方法:
register时,把方法存储在subscriptionsByEventType,post内部就是遍历所有的Class,到subscriptionsByEventType中去查找subscriptions,然后遍历subscriptions,对遍历到的每个subscription使用postToSubscription(subscription, event, postingState.isMainThread);方法。这个方法内部使用了反射去执行相应的方法。
另外,在register中,有一个参数sticky,为true时,会去Map集合stickyEvents中查找事件,然后立即去post。
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
}
这个方法和 post一样,只是将方法保存在了stickyEvents中。
面试------原理
答:
构造方法中通过单例模式初始化实例,内部维持了一个map对象存储了一堆方法,
注册的时候调用EventBus.getDefault().register(this); 传入的这个this就是源码中的Subscriber,然后通过反射,将@Subscribe注解的方法信息 封装到SubscriberMethod中,封装的内容包括Method对象、ThreadMode、事件类型、优先级、是否粘性等。
使用post进行发布,其实就是根据参数去map查找,进行反射调用。
一句话:register时会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。
(发布与消费事件: post进行发布 .然后到线程的队列中,然后进行消费事件,消费的时候通过反射和method 调用,这是回到queue队列当中,循环遍历queue中的event 查找可以消费该事件的类和方法,最终他会将事件交给这个类和方法,完成整个消息的发布与消费;)