软件工程大二学生一枚,对Android的理解不是很透彻,若有失误希望大家指正,谢谢大家。
在使用EventBus
的时候,觉得比较好用,所以就准备去看源码。本文主要介绍了EventBus
怎么实现的(从设计思想到每一行代码)。
总结为以下几点:
Subscriber
/Publisher
)的事件总线,内部是靠Handler
发送Message
来进行通信的。//SubscriberActivity
public class SubscriberActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_subscriber);
EventBus.getDefault().register(this);
}
public void onEvent(Event aEvent)
{
//do something with aEvent
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
//PublisherActivity
//可以在任意地方调用
EventBus.getDefault().post(new Event());
在EventBus
源码文件夹里面,首先可以看到2个类,EventBus
与EventbusBuilder
。
EventBus
定义了所有的入口方法;
EventbusBuilder
利用Builder模式来构造EventBus
。
上述例子中,调用了下面的方法:
static volatile EventBus defaultInstance;
/** * 双重同步锁,利用单例模式(Singleton)构造一个EventBus对象 */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
进入EventBus
的构造函数:
public EventBus() {
this(DEFAULT_BUILDER);
}
/** * 一大推参数暂时不解释,等哈在讲 * @param 为一个EventBusBuilder对象 */
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
typesBySubscriber = new HashMap<Object, List<Class<?>>>();
stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
EventBusBuilder
类里面:
/** * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be * done only once before the first usage of the default EventBus. * * @throws EventBusException if there's already a default EventBus instance in place */
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
/** Builds an EventBus based on the current configuration. */
public EventBus build() {
return new EventBus(this);
}
典型的构造者模式,不必多说,可以参见Notification
及NotificationBuilder
来学习。
我们可以通过以下方式来创建EventBus
对象:
EventBus.getDefault();
EventBusBuilder
自行定制参数,再来创建。Poster
上文说到:EventBus是一个发布 / 订阅(Subscriber/Publisher)的事件总线。那么Subscriber
Publisher
之间怎么联系,就要用到了Poster
。
Poster
有以下几种:
HandlerPoster
BackgroundPoster
AsyncPoster
重点介绍HandlerPoster
,其他2个差不多。
Step1:先看看PendingPost
final class PendingPost {
////单例池,复用对象
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event; //事件类型(就是onEvent(Event aEvent)里面这个参数的类型)
Subscription subscription; //订阅者的封装类
PendingPost next; //指向队列下个元素的指针
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
//首先检查复用池中是否有可用,如果有则返回复用,否则返回一个新的
//因为在池中数据都是null 所以只要池中有数据就拿出来用
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
//这里加锁,想想为什么?
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
//回收一个待发送对象,并加入复用池
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
// 池的最大限制
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
}
有以下几点说明:
PendingPost
内部维护了一个池,暴露的方法只有2个,obtainPendingPost
releasePendingPost
event
,subscription
,等哈再说。看作节点中的数据块就好了。Step2:PendingPostQueue
final class PendingPostQueue {
private PendingPost head;
private PendingPost tail;
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) { //第一次的时候调用
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();// 因为入队之后,队列中存在数据,唤醒下面的线程,即出队操作继续执行
}
synchronized PendingPost poll() {
PendingPost pendingPost = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
//当head == null的时候,该方法处于阻塞的状态。
//直到maxMillisToWait时间到了 或者 enqueue调用notifyAll()
if (head == null) {
wait(maxMillisToWait);
}
return poll();
}
}
队列没什么好说的,自己去看数据结构书去。然后wait() notify()
可以参考这些[Java 并发]多线程怎么进行同步(二)。
Step3:HandlerPoster
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive; //判断当前queue中是否有正在发送对象的任务
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
//入队方法会根据参数创建 待发送对象 pendingPost 并加入队列,如果此时 handleMessage() 没有在运行中,则发送一条空消息让 handleMessage 响应
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");
}
}
}
}
@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) { //当前消息队列中 没有PendingPost了
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;
}
}
}
注意几点:
Handler
的知识忘了,可以看这里。handlerActive
这个成员,是判断该Handler
里面的队列是否有消息正在处理。eventBus.invokeSubscriber(pendingPost);
这段代码才是真正的调用Subscriber
的方法。等哈在说。if (timeInMethod >= maxMillisInsideHandleMessage)
是为了防止,主线程阻塞,如果主线程出现了 ANR,就退出当前循环,然后再sendMessage(obtainMessage()
。对于AsyncPoster
BackgroundPoster
,里面调用下面这个函数:
eventBus.getExecutorService().execute(this);
ExecutorService
是Java1.5出来一个用来管理线程的一个工具类,通过操作java.util.concurrent里面的包而不是Thread
本身,可以更好的操作线程。
Subscriber
Step1:概念解释:
Subscriber
:可以把他理解为Register
,就是在本文一开始的代码中,调用EventBus.getDefault().register()
的类。Event
:就是一个事件, Subscriber
要对这个时间进行处理。Step2:EventBus
中有几个Map
是什么:
/** * key => event的Class类型(Event.class) * value => 可以响应该event的所有订阅者(onEvent*()函数的参数为event类型) */
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
/** * key => 订阅者 * value => 订阅者的所有的订阅方法 */
private final Map<Object, List<Class<?>>> typesBySubscriber;
/** * 粘性事件 */
private final Map<Class<?>, Object> stickyEvents;
注意:
stickyEvents
这里先不说,等哈在说Step3:register()
函数
private synchronized void register(Object subscriber, boolean sticky, int priority) {
//使用subscriberMethodFinder来查找subscriber中所有的SubscriberMethod(订阅方法,onEvent()等方法)
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
//循环每一个事件响应方法 将订阅者存储起来
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
里面出现了几个类:
SubscriberMethod
: 对订阅方法(在订阅者里面以onEvent开头的函数)的封装类。(后文直接称为订阅方法)
final class SubscriberMethod {
final Method method; //方法
final ThreadMode threadMode; //一个枚举类型,包括EventBus四种通知模式。
final Class<?> eventType; //所属的类的类类型
/** Used for efficient comparison */
String methodString;
//将上面参数进行处理
private synchronized void checkMethodString() {
if (methodString == null) {
// Method.toString has more overhead, just take relevant parts of the method
StringBuilder builder = new StringBuilder(64);
builder.append(method.getDeclaringClass().getName());
builder.append('#').append(method.getName());
builder.append('(').append(eventType.getName());
methodString = builder.toString();
}
}
}
subscriberMethodFinder
: 利用反射,找到一个类里面所有的SubscriberMethod
。基础知识,自己去看看反射的用法。
不过有2点要注意:
// 反射过程中,会搜索 ON_EVENT_METHOD_NAME 开头的函数,这就是为什么本文一开始提出的
// EventBus不是基于注解的,基于命名规定的,即以“onEvent”开头的,在这里可以修改。
private static final String ON_EVENT_METHOD_NAME = "onEvent";
//key => 类名
//value => 该类中所有的订阅方法
//因为反射比较耗时间,所以这里声明为static
private static final Map<Class<?>, List<SubscriberMethod>> methodCache = new HashMap<Class<?>, List<SubscriberMethod>>();
这样register()
方法就算可以了:利用反射找到Subscriber
内的所有的SubscriberMethod
,然后遍历,调用subscribe()
。
Step4:subscribe()
订阅方法的真正执行者,将订阅方法存储起来
这里用到了上面介绍的Map
,可以在这里加深印象(这段代码比较男懂)
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
// 构建 subscriptionsByEventType的映射
Class<?> eventType = subscriberMethod.eventType; //event的Class类型
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) { //第一次时调用
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
}
// 更具优先级来构建 subscriptions
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;
}
}
// 构造 typesBySubscriber
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) { //第一次时调用
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//粘性事件,暂时不说
if (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();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
Poster
Step1:Poster
就是调用EventBus.getDefault().post(new Event())
所属的类
Step2:post()
/** Posts the given event to the event bus. */
public void post(Object event) {
//得到当前线程下的 PostingThreadState
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
//当前的 postingState不再 postSingleEvent执行之下
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 {
//主要代码,当队列不为空时,将队列元素取出,执行postSingleEvent
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
上面代码出现的几个类:
PostingThreadState
: EventBus
静态内部类
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>(); //队列
boolean isPosting; //是否执行postSingleEvent
boolean isMainThread; //是否在主线程
Subscription subscription; //订阅者
Object event; //事件
boolean canceled;
}
currentPostingThreadState
:一个ThreadLocal<PostingThreadState>
对象,ThreadLocal
存储当前线程下的内容,自己Google。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
最后,队列中的每个元素,都会执行postSingleEvent
。
Step3:postSingleEvent()
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass(); //得到event的所属类的类类型
boolean subscriptionFound = false;
if (eventInheritance) { // 表示一个子类事件能否响应父类的 onEvent() 方法。
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));
}
}
}
说明一哈:
eventInheritance
设置响应可以继承,也就是一个子类事件能否响应父类的 onEvent()
方法,然后lookupAllEventTypes
利用递推,遍历得到所有的父类的onEvent()
。
Step4:postSingleEventForEventType()
/** * @param event 需要Post的事件 * @param postingState * @param eventClass event的Class类型 */
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 得到 能够响应 eventClass的所有订阅者
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 = subscriptionsByEventType.get(eventClass);
还记得 subscriptionsByEventType
这个Map
是干什么的吗?这句话的代码就是得到 可以响应eventClass的所有订阅者。Step5:postToSubscription
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);
}
}
这里才真正的把上文所有的内容贯通起来。首先,ThreadMode
是一个枚举类型。我们一个个来看:
PostThread
:订阅函数响应的线程和Poster
在一个线程。BackgroundThread
:当Poster
在主线程时,订阅事件就在新开线程,当Poster
不在主线程时,订阅函数就在当前线程执行。Async
:始终新开一个线程执行订阅事件,所以在这里可以执行耗时任务。MainThread
:始终在主线程执行,用于更新UI。 mainThreadPoster.enqueue(subscription, event);
backgroundPoster.enqueue(subscription, event);
asyncPoster.enqueue(subscription, event);
就是调用前文所说的Poster#handlerMessage()
。
最后总结一哈 EventBus
的原理:
何为粘性事件,我是从粘性广播(StickyBroadcast
)里面悟出来的。可以看这里
我的理解如下:
1. 首先调用post()
事件,EventBus
将事件存储起来。
2. 在调用register()
,EventBus
把存储起来的post()
在给订阅函数。
因为post()
没有消失,才称为粘性事件吧。
这样EventBus
给我们准备了几个特别的函数registerSticky()
postStivky()
同时使用stickyEvents
来存储事件。
/** * 粘性事件 */
private final Map<Class<?>, Object> stickyEvents;
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
最后,unregister()
类似,大家可以再次阅读源码理解更多。有什么问题咱们一起讨论。
EventBus
采用了Facade模式,即提供一个统一的接口给用户,用户只要写onEvent*()
方法,就可以被EventBus
识别,内部隐藏这些判断,用了EventBus
这个门面把具体的操作分发给了不同的SubSystem,也就是HandlerPoster
,MainThreadPoster
这些Poster
。这也就是门面模式的使用。
EventBus
采用事件驱动的思想。那么什么是事件驱动呢?说白了,就是一个事件A的调用时由于另一个事件B的触发而被调用,所以说是事件A是由事件B来驱动的。那么什么地方运用的最多呢?前段啊。就好说,一个按钮绑定一个事件,当用户点击这个按钮的时候(这是一个事件)这个按钮所绑定的事件就会被调用。
所以在EventBus
里面可以看作事件驱动的。当onEvent()
绑定了一个事件后,并没有直接被调用,而是由post
驱动的。而事件驱动器也就是EventBus.java
这个类,底层是Handler
。事件驱动将每个类的调用关系变得简单,没有那么多的回调函数。
原来在想事件驱动和异步是一个东西吗?当然不是啦。异步是我发消息之后(发送消息可以当作成一个事件),我就去做其他事,等你事做好了再来通知我。事件驱动是我做出了发送消息的事件会驱动你去做你要做的事。2者还是有微妙的区别的。
可以参考下面这些连接:
http://www.infoq.com/cn/articles/what-is-nodejs
http://www.infoq.com/cn/news/2011/09/nodejs-async-code
但是EventBus
也是有缺点的:
- 事件的订阅者又去充当发布者的话,会使得整个代码逻辑变得混乱。
- 一个订阅者可以接收到许多发送者的消息,但是有些消息是不想被订阅的。
事件驱动机制跟消息驱动机制相比