Android中EventBus介绍、使用及源码分析
1 介绍
EventBus是一款针对Android优化的发布/订阅事件总线。
主要作用
- 用于Android中不同组件不同线程之间的通信
- 简化了组件之间的通信
- 将事件发送者和接收者分开
- 与Activity,Fragments和后台线程执行得很好
- 避免了复杂和容易出错的依赖和生命周期问题
主要优点
- 使您的代码更简单
- 很快
- 很小(约50k)
- 具有高级功能,如传送线程,用户优先级等
三个核心概念
- Event:事件,用于定义事件的类型,可以是任意类型的对象。
- Subscriber: 事件订阅者,用于接受事件。
- Publisher:事件分发着,用于发出事件,以便让Subscriber接收。
2 简单使用之一 (普通事件)
1: 定义事件
public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; }}
2: 准备订阅者
声明并注解订阅的方法,指定一个线程模型
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {/* Do something */};
注册并取消注册订阅者。例如在Android上,Activity和Fragment通常应该根据其生命周期进行注册:
@Override public void onStart() { super.onStart(); EventBus.getDefault().register(this);//注册 } @Override public void onStop() { EventBus.getDefault().unregister(this);//注销 super.onStop(); }
3: 发送事件
从代码的任何部分发布事件。 所有匹配事件类型的订户都会收到。
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
3 简单使用之二(粘性事件)
除了上面讲的普通事件外,EventBus还支持发送黏性事件,就是在发送事件之后再订阅该事件也能收到该事件,跟黏性广播类似。即先发送事件后注册:
1: 订阅粘性事件
//注册事件
EventBus.getDefault().register(this);
2:订阅者处理粘性事件
在MainActivity中新写一个方法用来处理粘性事件:
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true) public void ononMoonStickyEvent(MessageEvent messageEvent){ }
3:发送黏性事件
EventBus.getDefault().postSticky(new MessageEvent("粘性事件"));
4 线程模型
EventBus可以为您处理线程,事件可以在与发布线程不同的线程中发布。一个常见的用例是处理UI更改。在Android中,UI更改必须在UI(主)线程中完成。另一方面,网络或任何耗时的任务不能在主线程上运行。 EventBus可以帮助您处理这些任务,并与UI线程同步(无需深入研究线程转换,比如使用AsyncTask等)。
Eventbus主要有5种线程模型,即:
POSTING,默认模式,订阅者将直接在和发布事件同一个线程中被调用。事件传递是同步完成的,所有订阅者一旦发布完成就被调用,该模式下传递事件开销最小,因为它完全避免了线程切换。因此这是简单任务下推荐的模式,比如已知可以在很短的时间内完成,而不需要主线程。使用此模式的事件处理程序必须快速返回以避免阻塞发布线程(可能是主线程)。
MAIN,在Android上,订阅者将在Android的主线程(UI线程)中被调用。如果发布事件的线程是主线程,则直接调用订阅者方法,阻塞发布线程。否则,事件排队等待传送(非阻塞)。使用这种模式的订阅者必须快速返回以避免阻塞主线程。如果不在Android上,则表现与POSTING相同。
MAIN_ORDERED,在Android上,订阅者将在Android的主线程(UI线程)中被调用。与MAIN不同,该事件将始终排队等待传送。这确保了推送事件是非阻塞的。例如,如果使用main线程模式在事件处理程序中发布另一个事件,则第二个事件处理程序将在第一个事件处理程序之前完成(因为它被同步调用 - 将其与方法调用进行比较)。使用MAIN_ORDERED,第一个事件处理程序将完成,然后第二个将在稍后的时间点被调用(只要主线程有能力)。
BACKGROUND,订阅者将在后台线程中被调用。如果发布线程不是主线程,则会在发布线程中直接调用事件处理程序方法。如果发布线程是主线程,则EventBus将使用一个后台线程来按顺序发送所有事件。使用这种模式的事件处理程序应该尽快返回以避免阻塞后台线程。
ASYNC,订阅者将在一个单独的线程中被调用。这总是独立于发布线程和主线程。发布事件永远不会等待使用此模式的订户方法。如果用户方法的执行可能需要一些时间,则它们应该使用这种模式。用于网络访问。避免同时触发大量长时间运行的异步订阅者方法来限制并发线程的数量。 EventBus使用线程池来有效地重用已完成的异步订户通知中的线程。
注意:otto 是Eventbus的”阉割”版本,而线程模型就是一大区别,换句话说,就是otto不能跨线程发送数据。EventBus实际上是包裹了一层 Handler
5 源码分析
首先我们从 获取EventBus对象开始:
/** Convenience singleton for apps using a process-wide EventBus instance. */ public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
通过上面的代码我们知道,获取EventBus对象实际上是单例模式获取。
再看下构造方法:
EventBus(EventBusBuilder builder) { logger = builder.getLogger();//获取打日志对象 subscriptionsByEventType = new HashMap<>();//定义订阅者里面订阅的事件类型 typesBySubscriber = new HashMap<>();//定义订阅者类型 键值对形式存储 stickyEvents = new ConcurrentHashMap<>();//存储粘性事件的集合 mainThreadSupport = builder.getMainThreadSupport();//对应主线程的一个封装类。主要包含一些判断当前线程是否是主线程,以及创建HandlerPoster等Handler子类方法进行跨线程传递消息 mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;// 当mainThreadSupport不为空的时候创建HandlerPoster backgroundPoster = new BackgroundPoster(this);//在 BACKGROUND 线程模式下维护事件管理,以及在该模式下发布事件 asyncPoster = new AsyncPoster(this);//在 ASYNC线程模式下 维护事件管理,以及在该模式下发布事件 indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0; subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex); logSubscriberExceptions = builder.logSubscriberExceptions; logNoSubscriberMessages = builder.logNoSubscriberMessages; sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; sendNoSubscriberEvent = builder.sendNoSubscriberEvent; throwSubscriberException = builder.throwSubscriberException; eventInheritance = builder.eventInheritance; executorService = builder.executorService; }
注意HandlerPoster类,该类继承Handler 并实现的发布事件的功能的接口:
public class HandlerPoster extends Handler implements Poster { private final PendingPostQueue queue;//维持一个队列,存储订阅者 及相应的事件 private final int maxMillisInsideHandleMessage; private final EventBus eventBus; private boolean handlerActive; protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { super(looper); this.eventBus = eventBus; this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; queue = new PendingPostQueue(); } //维持订阅者及相应的事件在一个队列中 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"); } } } } //处理发送来的各种消息 @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; } }}
我们现在先从发布事件开始 即post方法发布事件:
/** Posts the given event to the event bus. */ public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get();//获取当前线程中存贮的值 List
下面是postSingleEvent具体代码:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass();//获取事件的字节码 boolean subscriptionFound = false; if (eventInheritance) {// 默认true 目的是获取 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);//根据事件类型传递事件 具体代码见下 } 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)); } } }
查找所有事件类型
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */ private static List> lookupAllEventTypes(Class<?> eventClass) { synchronized (eventTypesCache) { List> eventTypes = eventTypesCache.get(eventClass);//以事件类型作为键查找 if (eventTypes == null) { //查找事件类型 如果不存在,那么就将当前事件类型存起来 eventTypes = new ArrayList<>(); Class<?> clazz = eventClass; while (clazz != null) { eventTypes.add(clazz);//存贮事件类型对应类的字节码 addInterfaces(eventTypes, clazz.getInterfaces());//存贮事件类型对应接口的字节码 clazz = clazz.getSuperclass(); } eventTypesCache.put(eventClass, eventTypes);//将事件类型存贮起来 } return eventTypes; } }
根据单个事件类型传递事件:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList subscriptions; synchronized (this) { subscriptions = subscriptionsByEventType.get(eventClass);//与订阅者关联的集合 } if (subscriptions != null && !subscriptions.isEmpty()) { //Subscription 订阅者的封装类 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; }
根据不同的线程模型,将事件发布给订阅者
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) {//根据线程模型 发送事件 case POSTING: invokeSubscriber(subscription, event);//就在当前线程处理 break; case MAIN: if (isMainThread) { invokeSubscriber(subscription, event);//在主线程处理 } else { mainThreadPoster.enqueue(subscription, event);//事件从主线程发送到子线程 mainThreadPoster既是HandlerPoster对象 将事件订阅者信息及事件发送给 HandlerPoster 里队列由HandlerPoster内部自动处理 } break; case MAIN_ORDERED: if (mainThreadPoster != null) { mainThreadPoster.enqueue(subscription, event);//同上 } else { // temporary: technically not correct as poster not decoupled from subscriber invokeSubscriber(subscription, event); } break; case BACKGROUND: if (isMainThread) { backgroundPoster.enqueue(subscription, event);//如果发送者是在主线程,那么该方法会在一个线程池中执行 backgroundPoster是实现runnnable 接口的类 原理同 mainThreadPoster } else { invokeSubscriber(subscription, event);//就在当前子线程处理 } break; case ASYNC: asyncPoster.enqueue(subscription, event);//不管发送事件在哪个线程发送的,该方法都在线程池中执行 asyncPoster 为AsyncPoster 实现了Runnable接口 break; default: throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); } }
BackgroundPoster 实现Runnable接口,里面的enqueue方法及run方法:
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);//将订阅者及事件放入线程池中执行 } } } @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; } }
AsyncPoster实现Runnable接口,里面的enqueue方法及run方法:
public void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); queue.enqueue(pendingPost);//队列管理 发布信息 eventBus.getExecutorService().execute(this);//将订阅者及事件放入线程池中执行 }public void run() { PendingPost pendingPost = queue.poll(); if(pendingPost == null) { throw new IllegalStateException("No pending post available"); } eventBus.invokeSubscriber(pendingPost); }
我们再看方法,invokeSubscriber,调用订阅者里的方法并传递event方法
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); } }
我们这时看订阅者(一开始注册rigister),有下面方法:
public void register(Object subscriber) {//传入的是订阅着所在的类 比如在Activity中注册了 EventBus.getDefault().register(this); 此时传入的就是Activity对象 Class<?> subscriberClass = subscriber.getClass();//获取订阅者所处类的字节码 List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//通过字节码获取所有订阅者方法 synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) {//遍历所有订阅者的的方法 subscribe(subscriber, subscriberMethod);//订阅 具体代码见下 } } }
subscribe方法如下:
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); if (subscriberMethod.sticky) { 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). Set, Object>> entries = stickyEvents.entrySet(); for (Map.Entry, 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); } } }
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { if (stickyEvent != null) { //粘性event 可以在不注册的时候 直接post // 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, isMainThread());//根据订阅者 及是否是粘性事件,是否是主线程 进行调用相应的线程模式下的订阅者方法 } }
总的来说,
register方法->拿到注册者所在类的字节码->拿到对应的字节码里所有方法,除去非订阅者的方法->确定线程模型->把所有订阅者方法收集起来,放入到方法集合(map集合)中方便post
post方法->拿到上面收集的方法的集合,拿到对应的方法->invoke调用相应的注册方法,传递Event事件
若有些讲解不当的还希望大家批评指正!
更多相关文章
- socket和HTTP在Android中的连接请求问题
- 基于线程池和NIO技术构建高效的多协议Android通讯框架
- android的线程安全
- Android的进程、线程与优先级
- Android用户界面开发:事件处理
- AsyncTask的使用半解--!
- android中消费事件的概念以及使onClick和onLongClick同时发生
- android中的多线程编程及消息机制
- Android--线程池实现方式解析