好久没写博文了,这几个月复习了很多东西,新年在老家闲的慌啊,我这么爱学习的人只能学习了哈啊哈哈哈哈哈哈(滑稽脸)。顺便总结总结一些容易忘的东西。
EventBus的使用相信大家都使的贼溜了。实在不行看文档嘛。本文只要是分析下EventBus的源码执行过程,分析分析设计的思路,这样对使用和深入学习甚至改进轮子都有好处。
这张是EventBus官网档下来的哈哈哈哈哈!
很明显啦,EventBus用的观察者模式,Publisher作为事件发布者(被观察者)把Event发布到中转服务站EventBus,EventBus把Event传给Subscriber事件订阅者(观察者)并且唤醒了它。
一. EventBus结构
根据EventBus源码,其架构可以分为5个部分
- 构造方法
- 订阅者注册
- 发送事件
- 根据不同线程参数等处理事件(用反射运行目标方法)
- 订阅者取消注册
1. 构造方法
EventBus注册的入口是EventBus.getDefault().register(this)
,
getDefault()
方法如下
static volatile EventBus defaultInstance;
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;
}
这里用了双重检查模式的单例模式。并且这里很高明的使用volatile 避免的指令重排序引起 未被完全初始化的问题。
什么?不懂重排序?
没事,重排序其实通俗的讲就是虚拟机为了优化指令,提高程序运行效率搞的东西。或者这么说,本来1,2,3三条指令按顺序运行,重排序之后那可能会变成1,3,2。这样会影响某些对象的初始化问题。
虽然效率双重检查模式会有一定影响运行效率,但是对同步代码天大的好处。
回头来,EventBus()的构造方法
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
这货,搞了个独生子EventBusBuilder(),让它全权代理各参数以及配置的存储(其实也就是设计模式中的Builder模式)
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
...省略一大堆妃子.....
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
2. 订阅者注册
接着到了register()方法
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
这里有俩个地方很关键,一个是1. findSubscriberMethods()方法
还有一个是2. subscribe()方法
,他们一个是为了找到订阅方法,一个是用来注册订阅方法。
1方法是为了找到订阅者传进来的所有订阅方法,当然里面还挺复杂的,还写了判断有没有缓存来提高效率。
findSubscriberMethods()
中用了findUsingInfo()
方法来获取订阅者的各种信息,并且进而调用了findUsingReflectionInSingleClass()
来反射获取订阅者中的方法还有将他们保存到findState里面。
总结起来1方法也就是找到所有订阅方法,保存订阅方法信息到findState
2方法是为了注册所有的订阅方法,当然还有粘性事件的处理,解析可以看下注释
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//根据订阅者和订阅对象创建订阅对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根据事件类型获取订阅对象集合
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(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;
}
}
.....
if (subscriberMethod.sticky) {
//如果是粘性事件
if (eventInheritance) {
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();
//从上面stickyEvents集合中获取该类型的事件发送给当前订阅者。下面else里头也一样
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
这里有点特别的地方就是,粘性事件的处理,如果是粘性事件,那么从粘性事件的集合中取出该事件发送到当前订阅者。
3. 发送事件
这里从currentPostingThreadState对象中取出事件队列并且将当前需要的事件保存到队列中,然后处理队列中的所有事件,哦,同时移除操作的事件
public void post(Object event) {
//事件队列和线程状态信息的Model类
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()) {
//如果队列不是空的,那么让postSingleEvent()处理事件,并且移除该事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
又回到postSingleEvent()
方法里,从这里开始向下,又是一系列的对方法的查找的算法(向上查找事件的父类),结果是查找到所有的父类事件后存在List中,然后通过postSingleEventForEventType(event, postingState, eventClass)
方法对各个事件分别处理,这里的函数名可以看出。。。。。
这里处理的关键在于postToSubscription(subscription, event, postingState.isMainThread);
中
另外,假如是粘性事件,那么,
从入口函数开始
EventBus.getDefault().postSticky(object);
它会把粘性事件的object放到下面这个Map里面
private final Map<Class<?>, Object> stickyEvents;
便于第2步的subscribe()
方法处理粘性事件
4. 根据不同线程参数等处理事件(用反射运行目标方法)
从第3步的最后那里,可以知道事件给了postToSubscription(subscription, event, postingState.isMainThread);
处理,这个方法很有意思,
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 MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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);
}
}
还记得使用EventBus定义处理事件的注解吗,也就是EventBus的4种线程模式,再看看上面的switch语句的处理,是不是遇到了父老乡亲。
这里根据不同的线程模式,来处理各个方法(反射执行方法),比如ThreadMode是MAIN,那么这里假如不是主线程的方法,则用Handler切换到主线程再执行。
5. 订阅者取消注册
取消注册那就是跟注册是相反的,大体原理一样,不过是变成了遍历取消注册
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
这里最关键的地方在遍历函数里头的
unsubscribeByEventType(subscriber, eventType);
它是为了从订阅对象集合里面逐个逐个移除subscriber(订阅者)
总结
我画了张思维导图,可能要放大才清楚咯