一、EventBus简介
EventBus README:github.com/greenrobot/…
官方文档:greenrobot.org/eventbus/do…
# EventBus is a publish/subscribe event bus for Android and Java.
# EventBus是一个事件发布/订阅框架。
复制代码
二、基础用法
举例:在MainActivity中,在用户填写姓名、年龄后点击确定,需要将用户信息发送到给另一个Activity展示出来,EventBus实现方法如下:
2.1 添加依赖
dependencies {
// eventbus
implementation 'org.greenrobot:eventbus:3.1.1'
}
复制代码
2.2 定义Event
首先,定义Event,即用户信息类如下:
public class User{
public String name;
public int age;
}
复制代码
2.3 Event Subscribers
事件订阅方;分为注册、处理、取消注册三部分,如果不取消注册会有内存泄漏问题;
事件订阅方可以是一个Activity/Fragment/View等,例如:
public class HelloActivity extends Activity {
@Override
public void onStart() {
super.onStart();
// 注册订阅方
EventBus.getDefault().register(this);
}
// 声明事件处理方法,threadMode表示在哪个线程去调用Subscribers
@Subscribe(threadMode = ThreadMode.MAIN)
public void welcomeUser(User user) {
CenterToast.makeText(this, "hello,"+ user.name, Toast.LENGTH_SHORT).show();
}
@Override
public void onStop() {
super.onStop();
// 取消注册订阅方
EventBus.getDefault().unregister(this);
}
}
复制代码
同样地,事件订阅方也可以是自定义类:
public class UserRequestManager {
public UserRequestManager {
// 注册订阅方
EventBus.getDefault().register(this);
}
// 声明事件处理方法,threadMode表示在哪个线程去调用Subscribers
@Subscribe(threadMode = ThreadMode.MAIN)
public void addUser(User user) {
UserRequest reqeust = new UserReqeust(user);
request.request();
}
public void release() {
// 取消注册订阅方
EventBus.getDefault().unregister(this);
}
}
复制代码
2.4 Event Publisher
事件发送方;
public class MainActivity extends Activity {
public void onConfirmClick() {
User user = new User();
user.name = nameTextView.getText();
user.age = Integer.parseInt(ageTextView.getText());
// 发送Event
EventBus.getDefault().post(user);
}
}
复制代码
三、ThreadMode
在声明事件处理方法的注解中,可以设置ThreadMode,表示当有该Event发送给订阅方时,在哪个线程去调用Subscribers,有五种ThreadMode可供选择:
3.1 ThreadMode: POSTING
默认设置,Subscribers在与事件发送者相同的线程被调用。事件发布是同步进行的,即事件发布后,所有事件订阅者Subscribers都会被依次调用;因为没有线程切换,这种执行方式开销最小;对于一些短时任务且不需要主线程可考虑这种方式;
3.2 ThreadMode: MAIN
Subscribers在主线程被调用。常用来在数据发生变化时更新UI,使用时要避免耗时任务阻塞主线程;
3.3 ThreadMode: MAIN_ORDERED
Subscribers在主线程被调用。MAIN_ORDERED与MAIN不同的地方是, MAIN_ORDERED会先进入队列并稍后发送给Subscribers,例如如果在某个Event中又发送了另外一个Event:
public class Demo1 {
@Subscribe
public void addUser(User user) {
Log.d("DemoTag", "demo1 post start");
EventBus.getDefault().post(user);
Log.d("DemoTag", "demo1 post end");
}
}
public class Demo2 {
@Subscribe(threadMode = ThreadMode.MAIN)
public void addUser(User user) {
Log.d("DemoTag", "demo2:" + user.name);
}
}
public class Demo3 {
@Subscribe(threadMode = ThreadMode.MAIN_ORDER)
public void addUser(User user) {
Log.d("DemoTag", "demo3:" + user.name);
}
}
复制代码
上述例子中,输出结果是:
>demo1 post start
>demo2:jack
>demo1 post end
>demo3:jack
复制代码
因为MAIN是同步执行,所以MAIN会在方法结束前执行,而MAIN_ORDERED会在方法执行完后再执行;
3.4 ThreadMode: BACKGROUND
Subscribers将在后台线程中被调用。
(1)如果POST线程不是主线程,则Subscribers将直接在后POST线程中被调用;
(2)如果发布线程是主线程,则EventBus将使用单个后台线程顺序传递所有事件,所以使用此模式的事件处理方法应快速返回以避免阻塞后台线程。
3.5 ThreadMode: ASYNC
Subscribers始终在单独的线程被调用,即不是POST线程也不是主线程。用于处理一些比较耗时的操作,EventBus内部使用线程池实现该模式的线程调度;
四、Sticky Events
对于一些需要保留较长时间的event,可以使用粘性事件。EventBus最后一个粘性事件保留在内存中,然后在一个新的subscriber初始化完成后就可以获取最新的event,或者可以显式查询最新的event事件。使用方法如下:
(1)sticky event publisher
EventBus.getDefault().postSticky(new User("jack", 18));
复制代码
(2)sticky event subscriber
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// 当Activity新建后,就会收到最新的event数据
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
textField.setText(event.message);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
复制代码
(3)显式查询event
public class Demo{
public void fun() {
User user = EventBus.getDefault().getStickyEvent(User.class);
if(user != null) {
// "Consume" the sticky event
EventBus.getDefault().removeStickyEvent(user);
// Now do something with it
}
}
复制代码
五、源码分析
源码分析从两个过程分析:
# 1.事件订阅过程
EventBus.getDefault().register(this);
# 2.事件发布过程
EventBus.getDefault().post("hello");
复制代码
5.1 EventBus.getDefault()
EventBus.getDefault()是一个单例模式:
public class EventBus {
static volatile EventBus defaultInstance;
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
// 订阅方法查找工具类:通过反射得到类名及方法名及注解,将对象和方法保存到hashmap中
private final SubscriberMethodFinder subscriberMethodFinder;
//
private final ExecutorService executorService;
// 单例模式
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
}
复制代码
5.2 订阅过程
订阅过程如下:
EventBus.getDefault().register(this);
复制代码
接下来分析register()源码:
public class EventBus {
// key是EventType,value是所有订阅该类型Event的Subscriber列表
// EventType即事件类型,如本文举例的User或String;
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// key是Subscriber对象,value是该对象所订阅的所有事件类型
// 与subscriptionsByEventType相对应
private final Map<Object, List<Class<?>>> typesBySubscriber;
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// subscriberMethodFinder通过反射得到类中所有被@Subscribe标记的方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 遍历把Subscribe方法根据EventType添加到对应的hashMap中
subscribe(subscriber, subscriberMethod);
}
}
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// ...
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
// 将对象按优先级添加到EventType所对应的订阅者list中
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// 将事件类型添加到Subscriber订阅的EventType list中
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
subscribedEvents.add(eventType);
// ...
}
// 取消注册过程原理类似,不再分析
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());
}
}
}
复制代码
5.3 发布过程
发布过程如下:
EventBus.getDefault().post("hello");
复制代码
接下来分析post()源码:
public class EventBus {
// TheadMode所对应的Poster;
// ThreadMode有5种,poster只有三种是因POSTING、MAIN是同步执行,不需要poster分发
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
/** 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;
try {
while (!eventQueue.isEmpty()) {
// post单个Event
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// ...
// 根据EventType
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
if (!subscriptionFound) {
// 没有找到subscriber...
}
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
// 根据EventType得到订阅者list
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
// 遍历list,将Event发送给订阅者
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
// 实际post方法
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);
}
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);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
void invokeSubscriber(Subscription subscription, Object event) {
// 最终通过反射调起Subscriber的事件处理方法
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);
}
}
}
复制代码
对于postToSubscription中ThreadMode: ASYNC模式,源码如下:
class AsyncPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
// 交给EventBusBuilder.DEFAULT_EXECUTOR_SERVICE的线程池去调度执行
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
// 最终通过反射调起subscriber的事件处理方法
eventBus.invokeSubscriber(pendingPost);
}
}
复制代码
六、annotation-processor
从源码分析可以看出,注册Subscriber的时候,是通过反射来查找用@subscribe注解标识的方法;除了这种方法外,EventBus还提供了annotation-processor的方式来处理注解,这样就省去了反射查找的过程。使用方法如下:
6.1 gradle配置如下
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
// 自定义annotation-processor生成的类名
arguments = [eventBusIndex: 'com.bc.demo.EventSubscriberInfoIndex']
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
复制代码
6.2 修改默认的EventBus
public class App extends Application{
@Override
public void onCreate() {
super.onCreate();
// 修改默认的EventBus
EventBus.builder().addIndex(new EventSubscriberInfoIndex()).installDefaultEventBus();
}
}
复制代码
之后,使用EventBus的流程和之前一样。