首先通过subscriberMethodFinder通过类型信息获取subscriber类中定义的onEvent***()方法列表,存储在subscriverMethods中。具体见2.2.7。
2.3.1.2 subscribe()
之后,调用subscribe方法
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//获取注册了消息类型(eventType)的注册者
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) {//如果没注册该消息类型
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {// 如果已经注册,此种情况为,针对同一个Subscriber对象(如Activity activity、Fragment fragment等)中调用了两次register()方法,无论是否是重载的register()方法
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)
// subscriberMethod.method.setAccessible(true);
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;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//按消息类型添加subscriber
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (sticky) { //??? what meaning
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);
}
}
}
2.3.1.2.1 CopyOnWriteArrayList的add(int, E)方法
public synchronized void add(int index, E e) {
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, index);
newElements[index] = e;
System.arraycopy(elements, index, newElements, index + 1, elements.length - index);
elements = newElements;
}
add(E)方法
public synchronized boolean add(E e) {
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, elements.length);
newElements[elements.length] = e;
elements = newElements;
return true;
}
可以看到CopyOnWriteArrayList与ArrayList的区别,ArrayList是声明一个定长数组,添加数据直接在数组里添加,当数目超过数组长度时,再声明一个更长的数组,将数据从旧数组copy到新数组里。而CopyOnWriteArrayList是ArrayList的特例,它的数组总是被填满的,每次添加数据时,都伴随着新数组的创建,而且新数组长度比旧数组长度只多一,因此每次添加操作都伴随着数据的复制。
2.3.1.2.2 sticky 参数
????
2.3.1.2.3
看下subscriptions.contains(newSubscription),其取决于ArrayList中的object是否equals给定的参数。Subscription类中重写了equals()方法。@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber && subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
因此如果针对相同的subscriber对象调用register()两次,无论是否为重载的,都会走到
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
。
2.3.2 unregister()方法
unregister()方法必须要调用,否则会造成内存泄露,具体原因见之前对typesBySubscriber的介绍。
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
整体思路就是把不需要再接收消息的Subscriber对象和处理方法(subscription)从HashMap中清除掉。比较有意思的是unubscribeByEventType()里for循环的写法。
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
首先,一个subsciptions列表中可以有多个subscription的subscriber与传入的subscriber相同,这是由于对相同的事件类型,可以有不同的threadMode,即一个Subscriber中,可以有onEventMainThread(EventA eventa),也可以有onEventBackgroundThread(EventA eventa)、onEventAsync(EventA eventa)、onEvent(EventA eventa)。
其次,在对List做遍历操作的同时更改List的内容,如果书写不当,是会抛出Concurrent异常的。此处的写法无问题,很值得借鉴。
2.4 post()与postSticky()
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get(); //获取当前线程中postMessage的状态
List<Object> eventQueue = postingState.eventQueue; //当前未完成的事件处理队列
eventQueue.add(event);
if (!postingState.isPosting) {
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()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
2.4.1 currentPostingThreadState是EventBus对象内部的ThreadLocal类型的变量,虽然是一个对象,但在不同的线程中调用get()方法,会返回该线程的PostingThreadState变量,每个线程的PostingThreadState都指向不同的对象句柄。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
2.4.1.1 这里用到了java的匿名内部类,实际上是创建一个继承于ThreadLocal类的对象,完整写法为:
class ThreadLocalSub<T> extends ThreadLocal {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
}
private final ThreadLocalSub<PostingThreadState> currentPostingThreadState = new ThreadLocalSub<PostingThreadState>();
2.4.1.2 看下ThreadLocal的get()方法
/**
* Returns the value of this variable for the current thread. If an entry
* doesn't yet exist for this variable on this thread, this method will
* create an entry, populating the value with the result of
* {@link #initialValue()}.
*
* @return the current value of the variable for the calling thread.
*/
@SuppressWarnings("unchecked")
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
其中,Thread对象内部存有Values对象, 如果Values为空,则创建一个,并赋值给Thread的Values对象;如果不为空,则在values.table中寻找当前ThreadLocal对象的值(Thread可以有多个ThreadLocal变量),如果找到,则直接返回;如果未找到,则创建,实际上调用的是ThreadLocal的initialValue方法(具体可见values.getAfterMiss(this)方法)。另外这是一种很好的解耦方式。
2.4.1.3 PostingThreadState类存储有当前线程中EventBus发送消息的相关信息,如事件队列、当前是否主线程、当前发送的消息、消息类型、是否被取消、是否在发送。
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
这里需要提个问题:为什么需要使用ThreadLocal变量,而不是一个全局的PostingThreadState对象?
答案是明显的,如果使用一个全局的PostingThreadState变量,那么多个线程中调用post()方法就必须进行同步操作,反复获取锁、丢弃锁,造成性能消耗;同时,post操作沦为一个顺序的操作,虽然可以多线程中调用,却只能一个接一个的发送。而使用ThreadLocal变量,每个线程中自己进行状态维护,可以并行执行,且不需要同步,性能消耗小。
但另一个问题来了,由于使用了ThreadLocal变量,所以post()代码只需要考虑单个线程执行的情况,那么使用isPosting变量进行锁限制,就很多余了。因为一个线程里,必然要执行完前一个方法,才执行下个方法。这是为什么呢?
2.4.2 post()方法主要调用了postSingleEvent()方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
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) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
postSingleEvent()方法又主要调用了postSingleEventForEventType()方法。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
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;
}
这里有个问题,如果subscriptions是ArrayList类型,对subscriptions的遍历不在synchronized语句块里,多线程情况下,应该有一定的概率会出现问题。
但是需要注意的是:subscriptions是CopyOnWriteArrayList类型的,它在java.util.cocurrent包中,在一个线程中对其遍历,在另外线程中,对其进行更改,是经过同步处理的。
最终调用的是postToSubsciption方法,结合之前说的mainThreadPoster、backgroundPoster、asyncPoster,很好理解。
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);
}
}
主要看下invokeSubscriber()方法,原理很简单,使用的是反射。
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);
}
}
三、 onEvent****(Object o)与onEvent****(具体的类型)比较,哪个效率更高?各有什么好处?
onEvent***(Object o)这种写法,在subscriptionsByEventType里,以Object.class(key)和subscribers(value)为键值对存储。
在eventInheritance为true时,post()任意EventType的消息,都会调用onEvent***(Object o)方法。而eventInheritance为false时,只有post(Object o)时,才会调用onEvent***(Object o)方法。与onEvent***(具体的类型)比较,自然是后者的效率高。EventBus的实现思路,其实就是通过类型信息来做事件区分的。