EventBus 源码分析
先上github 地址:eventbus
首先,说说Eventbus 是什么,能干什么
其为android中的事件总线框架,用于在app内不同模块间通信消息传递的框架。简单的说,就是一个地方发送一个广播,在注册了该广播的地方接受到广播(在对应的函数中)。
怎么使用?
1.需要创建一个类,该类对应着一类事件。(相当于广播中的,意图过滤器中的条件)
class MyEvent{} // 代表一类事件。 即一类的广播。
2.在需要相应的类中,进行注册。(对应的要在该类销毁的时候,进行注销操作)
EventBus.getDefault().register(this);// 注册该类为接受者
EventBus.getDefault().unregister(this);// 注销该类。
3.在注册的类中,添加对应接受事件的函数。比如
public void onEvent(MyEvent event) {
// TODO 在接收到该事件时,我们要进行的相应处理。
}
4.发送一个事件。也可以理解为发送一个广播。
EventBus.getDefault().post(new MyEvent());// 发送的MyEvent对象,会被注册了该事件的方法中得到回调。
简单的用法就是这些了。
eventbus 源码
我们就从其暴露给我们的接口来一步步看起源码。
首先看从getDefault方法开始。
// 这里可以看到一个典型的创建的单例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
在eventbus的构造函数中
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();// key 为所以Event的class,value为 该注册了该event的所以类。
typesBySubscriber = new HashMap<Object, List<Class<?>>>(); // key为注册event的类,value为该类中的所有event的class。 以上两 个集合非常重要,会频繁出现在下面的分析中。
stickyEvents = new ConcurrentHashMap<Class<?>, Object>(); // 保存粘性事件的集合。 key为event.class value为该class对应的对象实例。
// 以上三个map用来存放我们注册的事件相关的数据。
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
// 以上三个类,用来实现在不同线程中回调onEvent()函数。第一个为在通过handler实现主线程的回调。 后两个都是runnable的子类,是实现在子 线程中回调。
subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);// 该类为一个功能性辅助类,在该 类中主要实现在一个类中,查找出所以得 onEvent方法。
// 以下为以下简单的配置信息设置。
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
然后我们在看一下register(Object subscriber)方法,都会调用一个方法。
//subscriber 为注册的类,sticky 是否是粘性事件,priority为优先级 ,会影响存入map的顺序,进而影响回调的顺序。
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
首先,我们看到首先调用了SubscriberMethodFinder的findSubscriberMethods方法。给类做的事情就是 把 订阅了事件的类中的所有的onEvent回调函数给返回出去。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
synchronized (methodCache) {
subscriberMethods = methodCache.get(key);// methodCache 为一个static变量,注册的类为key,以该类中所有注册函数的list为value,相当于一级缓存。将所有的注册类中的注册方法保存下来。避免多次遍历类中方法的操作。
}
if (subscriberMethods != null) {
return subscriberMethods; // 如果已经处理过该类,直接使用缓存下来的数据返回。
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>(); // 保存已经添加的方法和该方法的参数拼接的字符串,用于防止重复添加。
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) { // 循环遍历该类,以及其父类
String name = clazz.getName();
//忽略掉系统中的类
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
// Skip system classes, this just degrades performance
break;
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { // 以OnEvent开始的方法
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //访问修饰符判断 public且非Ignone方法
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {// 只有一个参数的方法
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class SubscriberMethod类为封装了一个类中接收函数信息的封装类。其中包含了方法method、该方法需要回调的线程threadmode、对应的事件类型eventtype。
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}
}
clazz = clazz.getSuperclass(); // 获取父类的class,继续循环
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);//将该类中的所有对应的响应方法的集合缓存下来。
}
return subscriberMethods;
}
}
然后,遍历得到集合,调用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)方法。该方法 主要功能就是为了将我们在EventBus 构造函数中创建的subscriptionsByEventType、typesBySubscriber和stickyEvents这三个集合,将对应的数据保存在他们中。前面已经说过他们对应保存的数据,这里就不重复了。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType; // 事件的对应的class
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // 先从集合中获取以前保存的。
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);// subscription 类为对接受事件的类的包装类,里面包含 subscribe r 就是我们register(Object o)时传入的this。一般就是activity或fragment。subscribermethod 就是该类中的onevent方法的封装,priority
if (subscriptions == null) {//如果为空,代表第一次处理该event。创建list,并且将list添加到subscriptionsByEventType中。
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
//将newSubscription按照优先级添加到中。
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}// 到此,subscriptionsByEventType这个集合填充完毕。
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//同样的,先将这个类中之前event.class的集合取出。
if (subscribedEvents == null) { // 如果为空,代表是第一次,创建list集合,并且将其放到typesBySubscriber这个map中
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);// 将event加到刚才创建的list中。
if (sticky) { // 是否是粘性事件。如果不是的话,register方法到此就结束了。如果是注册的是粘性事件监听,就会在注册的时候,执行以下代码。
Object stickyEvent;
synchronized (stickyEvents) {
stickyEvent = stickyEvents.get(eventType); // 该集合中的stickyEvent是在postSticky(Object o)的时候,添加到集合中的。所以所谓的粘性事件就是在这类 注册粘性事件监听之前,就已经被post过的事件,在注册的时候会被直接调用,并将之前的Object作为参数传入。
}
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); // 这里是post粘性事件。该方法是post事件的核心方 法。之后在分析post模块的时候,会继续分析。
}
}
}
到这里,我们就算走完了全部register的流程。我们现在回顾一下注册所做的事情。其实,最主要的事情就是将注册的类(比如activity)中的对应的onevent回调回调函数(比如 OnEvent(MyEvent myevent)),以及其参数,通过自己定义的包装类,保存到相应的集合之中(subscriptionsByEventType,typesBySubscriber)。
然后,我们再看一看,我们是如何post一个事件是如何实现的。
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();//获得在和该线程有关的postingState对象。
List<Object> eventQueue = postingState.eventQueue; // 将event添加到postingState中的list中。
eventQueue.add(event);
if (!postingState.isPosting) { //根据postingState的状态,设置一下相应的状态。
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()) { // 将eventQueue中所有的对象post出去。
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
首先,我们先看一下PostingThreadState这个类。它是EventBus的内部类。其中主要是保存事件的一些信息,就是一个bean对象。这里需要特别关注一下currentPostingThreadState这个对象。它是ThreadLocal的实例。ThreadLocal可以理解一个用于在不同线程中保存数据的一个容器。比如你在主线程中添加了一条数据,又在子线程中添加了一条数据,然后,当你在主线程中去get的时候,拿到的就是你之前在主线程中保存的数据,如果你在子线程中去get,那么拿到的就是你在子线程中保存的数据。对于具体如何实现的,有兴趣的同学可以自己去研究研究,其中有用到很巧妙的算法。
然后我们在看看postSingleEvent(Object event, PostingThreadState postingState)方法.
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass(); // event对应的class
boolean subscriptionFound = false;
if (eventInheritance) { // 代表是否将event的父类,也进行处理。
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); // 在该方法中,通过循环,将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);
}
}
然后,其都会调用postSingleEventForEventType方法,返回的boolean代表是否存在对应的注册者类。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) { // 这里我们看到,我们通过eventClass从subscriptionsByEventType中拿到我们之间在注册的时候添加进去的该event的所对应的注册者的集合。
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {// 遍历该event 所有的注册者
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false; // 将信息保存到postingState对象中
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;
}
在这里然后调用 postToSubscription方法。该方法在之前就有遇到过。实在register时,对于粘性事件我们调用了该方法。现在我们就来看看到底该方法干了什么。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
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);
}
}
我们看到了,主要是通过区分subscription.subscriberMethod.threadMode的类型,以及当前方法执行的线程(也就是post方法调用的时候的线程),来调用不同方法。
这里主要有四种方式。 一种是invokeSubscriber(subscription, event) (其实最后调用的都是这个方法,不过是在不同线程中调用而已)代表在当前线程中调用。第二种 mainThreadPoster.enqueue(subscription, event),在主线程中调用。第三种 backgroundPoster.enqueue(subscription, event) 会在子线程中调用,而且该线程是维护了一个队列,依次处理所有的加入的数据。第四种是asyncPoster.enqueue(subscription, event)会开辟新的子线程在其中执行。
正如我刚才说的,其实最后调用的都是invokeSubscriber(Subscription subscription, Object event)这个方法。 只是通过在不同线程中调用,来实现在不同线程中回调。
void invokeSubscriber(Subscription subscription, Object event) {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
// 实现功能的就是该方法,通过反射调用该方法。
.......
}
现在我们来看看如何实现在不同线程中调用该方法。如果还记得的话,我们在eventbus的构造函数中,曾经创建过三个对象
mainThreadPoster,backgroundPoster,asyncPoster这三个对象,对应于不同线程。
首先,mainThreadPoster其实就是一个handler,而且是主线程中的handler,所以其对应的handleMessage方法会在主线程中执行。
首先调用了enqueue(Subscription subscription, Object event)方法。
首先,我们看PendingPost类,该也是一个封装event和subscription的类,但是不同的是其中包含一个pendingPostPool的存储集合,该集合里面添加进去的是我们之前创建过的PendingPost的对象。其作用是复用以前创建过的对象,减少对象创建的开销。
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null; // 将使用完毕的PendingPost中的数据清空
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost); 添加到集合中
}
}
}
在我们需要一个新的PendingPost的对象的时候
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) { //如果集合中有不用的PendingPost,将其取出,并且将对应的数据添加到其中。
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
然后,回到enqueue方法中,
void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); // 获得封装了subscription和event的pendingpost对象
synchronized (this) {
queue.enqueue(pendingPost); // 将其添加到自己定义的队列中
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) { // 调用sendmessage方法发送一个空消息,会调用handler的handlemessage方法中。
throw new EventBusException("Could not send handler message");
}
}
}
}
然后,来到handlemessage方法
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis(); //获取系统时间,相当于进入循环是的时间,因为我们看到下面是一个死循环,会用这个时间做一个限制。
while (true) {
PendingPost pendingPost = queue.poll(); //从队列中取出在enqueue中添加的数据。
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
} // 以上部分是为了判断队列是否为空的考虑线程安全的方法。
eventBus.invokeSubscriber(pendingPost); // 这里,就是实现真正功能的代码。我们看到,它又回到了EventBus类中,只不过,它是在主线程中调用的。因为这是在主线程的handler的handlemessage方法中。
long timeInMethod = SystemClock.uptimeMillis() - started; //又获取了一次系统时间,通过时间差,来控制循环。(当队列中数据好多时,才会用到)
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) { //如果此次循环已经超过规定时间,发送一个消息,会重新调用handlemessage方法。
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
我们再回到eventbus中看看invokeSubscriber方法
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription; // 将封装在PendingPost中的数据取出来。
PendingPost.releasePendingPost(pendingPost);// 会将PendingPost对象中的数据清空,放到其内部的集合中,以便将来再用。
if (subscription.active) {
invokeSubscriber(subscription, event);// 该方法,以前已经分析过了,就是通过反射回调到相应的方法中,完成post的实现。
}
}
到这里,我们就看完了在主线程中实现回调的实现了。对于BackgroundPoster和AsyncPoster其实现是类似的,唯一的区别就是其是实现Runnable接口,其是运行在子线程中的。其中也是先将数据添加到一个队列中,然后再其run方法中调用eventBus.invokeSubscriber(pendingPost)方法。这两个的区别就是 一个线程安全,一个线程不安全。
到此。post方法我们也算分析完毕了。对于unregister()方法,如果前面的都看懂了,那么unregister就很简单了,就是将之前register时保存到集合中的数据删除掉就好了。这里就不再累赘了。相信大家都可以轻松看懂的。