1.为什么会诞生EventBus
在Android开发过程中,我们经常有这样的需求:位于栈顶的Activity需要关闭非栈顶的Activity或调用其中的方法(比如刷新数据,更新UI等)
例:现在有3个Activity,分别是:主界面(A),个人中心(B),头像裁剪&上传页面(C),在这三个页面中都有一个显示用户头像的ImageView。当用户进入C页面选择图片裁剪并上传至后台服务器后。我们要保证ABC三个页面的头像显示一致,那么我们就来看看不用EventBus怎么实现这样的功能。
普遍的做法大概就这三种,如果还有其他方法,欢迎在评论区留言。
- 在AB两个Activity中暴露一个public static的方法,供C修改成功后调用。
- 在AB两个Activity中注册广播,当C修改成功后,发送对应的广播,完成修改。
- 通过startActivityForResult和onActivityResult来实现。
下面我们分析一下这三种方法的实现:
第一种:由于是静态方法,所以使用这种方法时,要将ImageView设置为全局静态变量,浪费宝贵的内存资源。
第二种:发送一个广播做通知,其实是一个不错的选择,发送广播然后实现广播类做回调通知。但是如果现在需求是对一个Fragment做通知。这个时候广播就派不上用场了。毕竟广播只能再Activity中注册发送广播,如果硬要这么做,又涉及到Fragment与Activity之间的数据交互问题。
第三种:代码量巨大并且逻辑有点绕,开发和维护成本都很大。
这三种方法,没有完全的对与错,好与坏,仁者见仁智者见智。
想想这些方法,真的难用啊,就没有方便快捷的方法吗?
滴滴滴,就在这个时候,一辆EventBus缓缓驶来,你发现驾驶员居然是一位神仙(old driver)。一种全局的观察者模式,你可以把你的愿望(Event)告诉(Post)这位神仙(old driver),他有很多小老弟(多线程),就算竭尽全力(不费吹灰之力)也要让你的愿望在你指定的地方实现。
2.下面我们看看EventBus怎么使用。
GitHub地址:
Android Studio Gradle配置:
compile 'org.greenrobot:eventbus:3.1.1'
Eclipse
卸载Eclipse,并安装Android Studio
最基础的使用方法
public class EventBusActivity extends AppCompatActivity {
@Subscribe(threadMode = ThreadMode.MAIN)
public void canBeAnyNameHereHaHaHa(MyTestEvent event) {
// 收到消息后打印
Toast.makeText(this, event.getMsg(), Toast.LENGTH_SHORT).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_bus);
// 第一步:注册订阅者身份
EventBus.getDefault().register(this);
// 第二步:发送消息给自己(双重身份:订阅者&发布者)
EventBus.getDefault().post(new MyTestEvent("Hello EventBus!"));
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销之前先判断是否已注册
if (EventBus.getDefault().isRegistered(this))
// 关闭页面的时候,一定要注销,不然会造成内存泄漏
EventBus.getDefault().unregister(this);
}
}
我们结合上面提到的修改头像为例,来看看用EventBus应该怎么做
- 定义通知实体类(也可不定义,使用String,Integer等任意类型都可以)
/**
* Created by ZengCS on 2018/8/17.
* E-mail:zcs417327734@gmail.com
*/
public class UpdateAvatarEvent {
private String url;// 头像云端地址
public UpdateAvatarEvent(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
- 在需要订阅通知的地方进行注册和注销,并写一个订阅方法(方法需要写注解 @Subscribe(threadMode = ThreadMode.XXX))
每个订阅者可以订阅多种通知类型,没有限制。
官方推荐注册&注销方式:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
但是官方推荐的方式经常出现灵异事件,所以大牛们还是推荐这样写:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 注册EventBus
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销之前先判断是否已注册
if (EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
}
下面我们看看在主页和个人中心如何订阅修改头像的通知:
/**
* Created by ZengCS on 2018/8/17.
* E-mail:zcs417327734@gmail.com
*/
public class IndexActivity extends AppCompatActivity {
private ImageView mImageView;
@Subscribe(threadMode = ThreadMode.MAIN)
public void onUpdateAvatarEvent(UpdateAvatarEvent event) {
if (event == null) return;
String url = event.getUrl();
if (!TextUtils.isEmpty(url))
Glide.with(this)
.load(url)
.into(mImageView);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
mImageView = findViewById(R.id.id_iv_avatar);
// 注册EventBus
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销之前先判断是否已注册
if (EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
}
}
/**
* Created by ZengCS on 2018/8/17.
* E-mail:zcs417327734@gmail.com
*/
public class UserCenterActivity extends AppCompatActivity {
private ImageView mImageView;
@Subscribe(threadMode = ThreadMode.MAIN)
public void onUpdateAvatarEvent(UpdateAvatarEvent event) {
if (event == null) return;
String url = event.getUrl();
if (!TextUtils.isEmpty(url))
Glide.with(this)
.load(url)
.into(mImageView);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_center);
mImageView = findViewById(R.id.id_iv_avatar);
// 注册EventBus
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销之前先判断是否已注册
if (EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
}
}
- 在任意地方调用EventBus.getDefault().post(object)
/**
* Created by ZengCS on 2018/8/17.
* E-mail:zcs417327734@gmail.com
*/
public class EditAvatarActivity extends AppCompatActivity {
// 非相关代码已删除,这里假装头像上传成功了,并把云端头像地址返回给我们了
public void onUploadSuccess(String avatarUrl) {
// 发送更新头像通知
EventBus.getDefault().post(new UpdateAvatarEvent(avatarUrl));
}
}
如此方便快捷的使用方式,确实是做APP层消息通知的不二选择。不过在注册过EventBus的类中,千万别忘了在结束的时候注销。
这么好用的东西还是需要去了解一下其内部实现的。而且EventBus内部实现其实并不复杂。
3.EventBus源码解析
获取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;
}
可以看到,这是一个典型的DCL单例模式。单例模式获取一个EventBus对象,保证所有的EventBus对象是同一个。
下面我们来看看构造函数:
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
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;
}
两个构造方法,一个是无参的public的方法,另外一个是内部方法,带参数的。
内部构造方法中创建了几个容器,用来装各种订阅者信息的,很关键的几个成员我们需要注意,mainThreadPoster 、backgroundPoster 、asyncPoster ,这个几个是后来post消息的时候,用的到的,就是利用他们将消息在不同的线程发送出去,达到在不同线程中执行的效果。
这边DEFAULT_BUILDER声明如下:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
我们看看EventBusBuilder在做什么:
/*
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.greenrobot.eventbus;
import android.os.Looper;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Creates EventBus instances with custom parameters and also allows to install a custom default EventBus instance.
* Create a new builder using {@link EventBus#builder()}.
*/
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
boolean eventInheritance = true;
boolean ignoreGeneratedIndex;
boolean strictMethodVerification;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
Logger logger;
MainThreadSupport mainThreadSupport;
EventBusBuilder() {
}
/** Default: true */
public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
this.logSubscriberExceptions = logSubscriberExceptions;
return this;
}
/** Default: true */
public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
this.logNoSubscriberMessages = logNoSubscriberMessages;
return this;
}
/** Default: true */
public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
return this;
}
/** Default: true */
public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
this.sendNoSubscriberEvent = sendNoSubscriberEvent;
return this;
}
/**
* Fails if an subscriber throws an exception (default: false).
* <p/>
* Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, you won't miss
* exceptions during development.
*/
public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
this.throwSubscriberException = throwSubscriberException;
return this;
}
/**
* By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified).
* Switching this feature off will improve posting of events. For simple event classes extending Object directly,
* we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be
* >20%.
* <p/>
* However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app,
* unless it is posting at high rates, e.g. hundreds/thousands of events per second.
*/
public EventBusBuilder eventInheritance(boolean eventInheritance) {
this.eventInheritance = eventInheritance;
return this;
}
/**
* Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced
* setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior.
*/
public EventBusBuilder executorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
/**
* Method name verification is done for methods starting with onEvent to avoid typos; using this method you can
* exclude subscriber classes from this check. Also disables checks for method modifiers (public, not static nor
* abstract).
*/
public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
if (skipMethodVerificationForClasses == null) {
skipMethodVerificationForClasses = new ArrayList<>();
}
skipMethodVerificationForClasses.add(clazz);
return this;
}
/** Forces the use of reflection even if there's a generated index (default: false). */
public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
return this;
}
/** Enables strict method verification (default: false). */
public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
this.strictMethodVerification = strictMethodVerification;
return this;
}
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
/**
* Set a specific log handler for all EventBus logging.
* <p/>
* By default all logging is via {@link android.util.Log} but if you want to use EventBus
* outside the Android environment then you will need to provide another log target.
*/
public EventBusBuilder logger(Logger logger) {
this.logger = logger;
return this;
}
Logger getLogger() {
if (logger != null) {
return logger;
} else {
// also check main looper to see if we have "good" Android classes (not Stubs etc.)
return Logger.AndroidLogger.isAndroidLogAvailable() && getAndroidMainLooperOrNull() != null
? new Logger.AndroidLogger("EventBus") :
new Logger.SystemOutLogger();
}
}
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;
}
}
/**
* 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);
}
}
里面是一些EventBus的基础配置,打印异常信息,发送未被订阅的消息等。
特别需要注意的是 ignoreGeneratedIndex 这个变量是用来确定EventBus用什么方式来获取订阅方法。
true代表的是不优先使用索引,而用反射的方式,false代表的是优先使用索引。默认是false。
这边有个addIndex方法是用来添加订阅方法索引的,这是3.0版本的新特性,也是高级用法。
在EventBus带参数的构造函数里面初始化了 SubscriberMethodFinder 这个类,三个参数分别为索引列表,
是否严格方法定义,是否无视索引。
SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
boolean ignoreGeneratedIndex) {
this.subscriberInfoIndexes = subscriberInfoIndexes;
this.strictMethodVerification = strictMethodVerification;
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}
getDefault总结:获取一个EventBus的单例对象,用于任何EventBus相关的操作,比如:注册,注销,发送等。
注册
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
首先是通过一个findSubscriberMethods方法找到了一个订阅者中的所有订阅方法,返回一个 List,然后遍历所有的订阅方法,做订阅操作。
进入到findSubscriberMethods看看如何实现的。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
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 {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
我们来看看METHOD_CACHE是怎么实现的
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
这里使用了ConcurrentHashMap来对所有的方法进行缓存,为什么要使用ConcurrentHashMap来做方法的缓存呢?无疑是看中了ConcurrentHashMap线程安全且效率高的特性,更多关于ConcurrentHashMap的特性,请自行查阅相关资料。
回到findSubscriberMethods中,首先是通过类来当key,查找在当前缓存中是否存在这个类的订阅方法列表。有就直接return 缓存中的列表。
没有就进入下一步,这边有两种方式来获取类中的订阅方法通过ignoreGeneratedIndex来确定获取订阅方法的方式为true时会使用反射方法获取订阅者的事件处理函数,为false时会优先使用subscriber Index(订阅方法索引)生成的SubscriberInfo来获取订阅者的事件处理函数(减少反射耗时),默认false。
使用订阅方法索引需自己构建一个EventBus对象,本文暂时不讲解索引方式。
下面我们进入findUsingReflection看看是如何用反射获取到订阅方法的
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
解读:
- 获取订阅类声明的所有方法; 然后对获取到的方法全部遍历一遍
- 获取方法的修饰符:即方法前面的public、private等关键字。
- 如果该类方法使用了@Subscribe标注、方法中只有一个参数、且方法修饰符为public。findState.checkAdd(method, eventType) 如果之前没有存在过则返回true
- 判断@Subscribe标注中的threadMode对应的值,默认模式ThreadMode.POSTING
- 创建一个SubscriberMethod(普通的JavaBean)对象,该对象很简单就是保存有方法、方法参数类型、线程模式、订阅的优先级、sticky标志位。并将该对象添加到FindSate的List集合中。
订阅
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> 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<Class<?>> 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<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);
}
}
}
解读:
- 获取方法参数类型;注意:使用@Subscribe标注的方法有且仅有一个参数
- 利用订阅者对象及其事件处理方法构建一个Subscription对象,该对象存储有Object、SubscriberMethod对象
- 从subscriptionsByEventType集合中获取当前事件对应的Subscription对象集合; 如果得到的集合为空则创建一个这样的集合,并将刚创建的Subscription对象添加进subscriptionsByEventType集合中;如果得到的集合不为空且刚创建的Subscription对象已经存在该集合中则抛出异常,即同一个对象不能注册两次!
- 将第二步创建的Subscription对象按照优先级存入Subscription对象集合中,该集合中的元素都是按照优先级从高到低存放.
- 以subscriber对象为键,从typesBySubscriber获取该对象对应的接收事件类型集合,没有则创建一个这样的集合,然后当前事件类型添加到该集合中,最后将整个集合添加进typesBySubscriber集合中;有则直接添加到接收事件类型集合中;
- sticky 该值默认为false,除非在注册事件方法时使用了如下的标注@Subscribe(sticky = true);那么就会执行到这里。stickyEvent也是EventBus3.0的一大特点,该类事件一旦发送给EventBus,那么EventBus就会将它存入Map
发布订阅事件
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
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;
}
}
}
- 获取当前线程对应的一个PostingThreadState()对象
- 向PostingThreadState的事件队列中添加一个事件
- 从PostingThreadState的事件队列——eventQueue中移出一个事件,并调用postSingleEvent方法进行派送
下车
/** 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) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
- 从typesBySubscriber获取该对象接收的事件类型集合;
- 对得到的接收事件类型集合中的每个事件类型调用unsubscribeByEventType进行处理;跟着我们就分析该方法
- 该对象从typesBySubscriber集合中移除;
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(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--;
}
}
}
}
- 从subscriptionsByEventType集合中获取该事件类型对应的Subscription集合
- 如果集合中的元素——Subscription的subscriber域等于目标subscriber,则将该Subscription从subscriptionsByEventType集合中移除出去;
没有精确去分析所有的源码。这边大概做一下总结,整个流程大概如下。
- 注册EventBus,EventBus会获取当前类中的订阅方法,包括方法的参数,类型,优先级等信息。在获取的方法中,会先遍历缓存。如果在缓存中没有才去调用获取方法。
- 获取订阅方法有两种方式,一种是反射,反射获取全部方法,找到加了@Subscribe注解并有一个参数的方法,另外一种是通过订阅方法索引来得到订阅方法。反射的效率较低,所以优先使用用订阅方法索引来获取,当索引列表找不到这个方法。它就会走反射的途径。注册过后这个订阅方法就被存起来了。等待接受消息。
- 在3.0以前是没有注解方法的那时候都是通过反射来获取和执行方法。而且方法必须以onEvent开头,分别为onEventMainThead,onEventBackground,onEvent,onEventAsync,3.0以后就全部替换为注解的形式,方法名可以是任意(只要能通过编译的都可以)
- postEvent,会把这个Event加入消息队列,然后通过Event的类型信息来得到它的订阅方法,然后根据相应的订阅方式反射调用订阅方法。没有选择的threadMode的post一般都会在UI线程执行。如果当前post不是UI线程,这边会用Handle的机制来让接受Event的方法运行在UI线程。
- 解除注册,将该对象从对象缓存列表中移除,获取当前对象的订阅列表,然后将其从订阅列表移除