EventBus原理+源码解析(图文并茂)
来源:互联网 发布:京都 知乎 编辑:程序博客网 时间:2024/05/29 02:50
软件工程大二学生一枚,对Android的理解不是很透彻,若有失误希望大家指正,谢谢大家。
动机
在使用EventBus
的时候,觉得比较好用,所以就准备去看源码。本文主要介绍了EventBus
怎么实现的(从设计思想到每一行代码)。
什么是EventBus
总结为以下几点:
- EventBus是一个发布 / 订阅(
Subscriber
/Publisher
)的事件总线,内部是靠Handler
发送Message
来进行通信的。 - EventBus不是基于注解的,基于命名规定的,即以“onEvent”开头的。
- 事件驱动
- EventBus可以在多线程下订阅消息。
上面这几点,下面都会一一解释的。
先看一段EventBus的简单使用代码:
//SubscriberActivitypublic 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
在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()
。
来张图梳理一哈,上面3个类之间的关系:
对于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 => 该类中所有的订阅方法//因为反射比较耗时间,所以这里声明为staticprivate 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); } } }
OK,Subscriber
总算是完成了。
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。
这也印证了一开始说的 EventBus可以在多线程下订阅消息。
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
也是有缺点的:
- 事件的订阅者又去充当发布者的话,会使得整个代码逻辑变得混乱。
- 一个订阅者可以接收到许多发送者的消息,但是有些消息是不想被订阅的。
事件驱动机制跟消息驱动机制相比
邮箱:1906362072@qq.com
- EventBus原理+源码解析(图文并茂)
- EventBus框架原理解析(结合源码)(上)
- EventBus框架原理解析(结合源码)(下)
- EventBus源码解析(二)
- EventBus(2)——源码解析
- EventBus的源码解析(一)
- EventBus 源码解析
- EventBus 源码解析
- EventBus 源码解析
- EventBus 源码解析
- EventBus 源码解析(一)
- android EventBus源码解析
- EventBus 源码解析
- EventBus源码解析
- Android 源码解析:EventBus
- 源码解析EventBus
- EventBus 源码解析
- EventBus源码解析
- 三层登录实现
- Babylonjs入门--问题小结
- java中(&,&&)和或(|,||)的区别
- [Head First Java] 一个简单的聊天程序
- excel使用操作
- EventBus原理+源码解析(图文并茂)
- iOS7.0 修改状态栏字体的颜色
- fibonacci的几种实现
- 【MIG专项测试组】如何准确评测Android应用的流畅度?
- 广东灏瀚科技icare3.0系统学习之旅---icare3.0用法字典的维护
- 移动端轻量级数据库SQLite以及FMDB框架的一些总结
- 搭建apache + php + mysql服务器文件下载地址
- The Tao Of Programming
- ANDROID UI 抽屉效果