Android开源项目原理系列
[搞定开源] 第一篇 okhttp 3.10原理
[搞定开源] 第二篇 okio 1.14原理
[搞定开源] 第三篇 retrofit 2.4.0与设计模式
[搞定开源] 第四篇 手动实现RxJava2线程切换
EventBus是Android的发布订阅开源库,优势和使用方法不用说了,简单过一下源码,了解核心原理。
当前EventBus最新版本是3.1.1,和前代相比,方法名可以自定义,新增注解支持。
EventBus构造函数
优秀开源库的基本特征是,常用功能用一个入口类就可以玩转,EventBus也是。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
获取EventBus对象常规使用getDefault,典型的Double Check获取单例。如果有特别需求,可以通过EventBusBuilder,典型的建造者模式。
EventBus构造函数创建了十多个对象,关注其中的:
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
//...
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
//...
}
Log
EventBus的日志打印使用Logger接口,已经实现了:
- AndroidLogger
- JavaLogger
- SystemOutLogger
static final boolean ANDROID_LOG_AVAILABLE;
static {
boolean android = false;
try {
android = Class.forName("android.util.Log") != null;
} catch (ClassNotFoundException e) {
// OK
}
ANDROID_LOG_AVAILABLE = android;
}
public static boolean isAndroidLogAvailable() {
return ANDROID_LOG_AVAILABLE;
}
对于Android,直接尝试获取Log类,同时用来判断当前是否Android。
获取Android的UI线程
EventBus如何让事件执行在Android的UI线程呢?
MainThreadSupport getMainThreadSupport() {
if (mainThreadSupport != null) {
return mainThreadSupport;
} else if (Logger.AndroidLogger.isAndroidLogAvailable()) {
Object looperOrNull = getAndroidMainLooperOrNull();
return looperOrNull == null ? null :
new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
} else {
return null;
}
}
Object getAndroidMainLooperOrNull() {
try {
return Looper.getMainLooper();
} catch (RuntimeException e) {
// Not really a functional Android (e.g. "Stub!" maven dependencies)
return null;
}
}
这里用到了上面判断Android的方法,如果是,创建AndroidHandlerMainThreadSupport,保存了Looper.getMainLooper()。很熟悉吧,这就是Android的UI线程Looper。
Poster
EventBus的event可以指定运行在UI线程或者后台线程,靠的就是Poster,下文会说到,透露一丢丢:
- UI线程:MainLooper
- 后台线程:线程池
注册/取消注册
想要接收EventBus发送的事件,对象必须先注册,停止接收时取消注册。注册过程,本质是通知EventBus查找对象中的订阅方法。
Subscribe注解
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent4Main(MsgEvent event) {}
EventBus3之后,订阅方法名改为自定义,通过注解描述。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
除了基础的ThreadMode线程模型,还可以定义粘性事件和优先级。
注解信息,在注册过程最后由SubscriberMethod类描述。
注册过程
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
注册时传入订阅对象,两部操作:
- 分析订阅对象的类,SubscriberMethodFinder从中查找订阅方法,包装为SubscriberMethod。
- 调用subscribe,数据转化为EventBus内部的两个Map。
List
List findSubscriberMethods(Class> subscriberClass) {
//1
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//2
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//3
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
- 查找目标类的订阅方法前,先查找缓存METHOD_CACHE,用的是ConcurrentHashMap,因为要处理多线程。
Map, List> METHOD_CACHE = new ConcurrentHashMap<>()
- 默认创建的EventBus对象ignoreGeneratedIndex=false,先看findUsingInfo
- 获取List成功后,保存在缓存METHOD_CACHE
SubscriberMethodFinder.FindState
上面第二步重点是使用SubscriberMethodFinder的内部类FindState执行查找过程:
private List findUsingInfo(Class> subscriberClass) {
//1
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
//2
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//3
findUsingReflectionInSingleClass(findState);
}
//4
findState.moveToSuperclass();
}
//5
return getMethodsAndRelease(findState);
}
第一步是调用prepareFindState获取FindState:
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
FindState的创建不是直接new,而是尝试从FIND_STATE_POOL中获取(最多保存4个),目测是享元模式,没有才new对象。
第二步进入循环,从当前类开始,遍历父类查找订阅方法。
这里涉及EventBus3新特性,运行时反射查找方法当然会慢点,可以使用Subscriber Index在编译期操作加速,具体不展开,直接看else部分。
第三步的findUsingReflectionInSingleClass,使用反射查找,细节不用展开。
第四步将当前查找目标切换到父类,继续循环查找。
private List getMethodsAndRelease(FindState findState) {
List subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
第五步清理FindState,放回FIND_STATE_POOL,下次继续利用。
subscribe
register函数对每个SubscriberMethod执行subscribe,注意这里synchronized当前EventBus对象。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ 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;
}
}
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//... 粘性事件略
}
subscribe不外乎是对数据的挪腾,目的是构建subscriptionsByEventType和typesBySubscriber两个Map:
subscriptionsByEventType
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
Subscription包装了订阅对象和订阅方法,因此Map的key/value对应如下:
event类型 -> List<订阅对象,订阅方法>
表示了event由哪些对象的哪个方法订阅了。
typesBySubscriber
private final Map
typesBySubscriber简单些,表示订阅对象订阅了哪些event类型:
订阅对象 -> List
取消注册
public synchronized void unregister(Object subscriber) {
List> 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());
}
}
取消注册的逻辑很简单,将当前订阅者的信息从subscriptionsByEventType和typesBySubscriber中移除。
发送
PostingThreadState(每个线程一份)
EventBus发送event调用post方法,首先来认识EventBus核心的内部类PostingThreadState。
private final ThreadLocal currentPostingThreadState = new ThreadLocal() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
final static class PostingThreadState {
final List
PostingThreadState包含eventQueue和一些标志变量,通过ThreadLocal在每个线程中维护一个对象(ThreadLocal的initialValue方法初始创建PostingThreadState对象)。
没有深奥的玄妙,看数据结构大概可以知道,EventBus发送原理是将event入队在线程上的PostingThreadState,再查找订阅者发送。
event进出队
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
发送event的逻辑很明确,取出当前线程的PostingThreadState,将event入队。当不在发送状态时,循环调用postSingleEvent发送所有event。
发送event
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//1
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List> 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);
}
//2
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
第一部分先收集event的类型,eventInheritance在EventBus默认构造函数里为true,表示会向上遍历event的父类。然后调用postSingleEventForEventType执行发送过程,得到发送结果subscriptionFound。
第二部分,如果找不到订阅者,EventBus将会发送NoSubscriberEvent这个事件,业务上可以接收处理。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList 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;
}
postSingleEventForEventType根据event类型从subscriptionsByEventType获取所有订阅者,对每个订阅者执行postToSubscription。
订阅方法各有执行线程的要求,postToSubscription其实就是对各个线程模型的分开处理,下面分析常用的POSTING、MAIN和BACKGROUND。
POSTING
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);
}
}
POSTING,在哪个线程发送,就在哪个线程处理事件,直接反射invoke。
MAIN
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
如果当前已经在UI线程,直接调用invokeSubscriber,否则调用mainThreadPoster将event入队。
前面分析了AndroidHandlerMainThreadSupport保存了UI线程的Looper,通过Looper创建HandlerPoster,mainThreadPoster正是HandlerPoster的对象。有了UI线程的Handler,接下来的都是顺理成章的代码。
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
订阅者信息Subscription和event包装为PendingPost,通过next字段,多个PendingPost构建队列PendingPostQueue。
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
通过发送空message,切换线程到UI线程,从队列中取出event调用invokeSubscriber。注意到,在UI线程有执行时长限制,超时会抛出异常。
BACKGROUND
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
- 如果事件在UI线程中发布,那么该事件就会在新的线程中运行
- 如果事件在子线程中发布,那么该事件直接在发布事件的线程中执行
BackgroundPoster处理事件在新线程中的运行,同样的创建PendingPost并入队。
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
线程池从EventBus对象获取,看定义,是一个线程数几乎无限制的CachedThreadPool。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
最终在线程池的执行过程也是PendingPost出队,调用invokeSubscriber。(代码嵌套很深!)
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
后记
EventBus源码没有太难的地方,快速通过。