为了简化并且更加高质量地在Activity、Fragment、Thread和Service等之间的通信,同时解决组件之间 高耦合的同时仍能继续高效地通信,事件总线设计出现了。提到事件总线我们会想到Eventbus和otto。
otto 是 Square公司发布的一个发布-订阅模式框架,它基于Google Guava 项目中的event bus模块开发, 针对Android平台做了优化和加强。Square已经停止了对otto的更新并推荐使用RxJava和RxAndroid来替 代它。
EventBus是一款针对Android优化的发布-订阅事件总线。它简化了应用程序内各组件间、组件与后台线 程间的通信。其优点是开销小,代码更优雅,以及将发送者和接收者解耦。如果Activity和Activity进行交互 还好说,但如果Fragment和Fragment进行交互则着实令人头疼。这时我们会使用广播来处理,但是使用广播 略嫌麻烦并且效率也不高。如果传递的数据是实体类,需要序列化,那么传递的成本会有点高。
Android中模块更新的机制有很多种,EventBus又是其中使用起来较为方便简单的一种,本文就针对EventBus在平常工作中的用法,做一个个人总结。主要分为以下几点来展示:
- 介绍
- 源码分析
- 用法示例
下图也就是本文要讲的主要内容:
1、介绍:
EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低。我们可以在要进行实时界面更新的交互上使用该模块以替代一些消息机制的处理。EventBus的·优点是用法简单,不必写繁琐的代码,而且耦合度也低。缺点其实个人发现无明显的缺点,可能就是得针对每个事件定义也一个事件类,感觉会造成重复造轮子的感觉。此外,关于事件总线还有的一个框架是Otto,这边就不分析这个框架了。
2、源码分析:
按照惯例,我们在代码里面查看EventBus.java的源码,这里就不贴出整个类的源码了。我们首先看下,该类的构造方法:
/**
* 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) {
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
主要是一些要用到类的对象的初始化,用的是Builder模式。
注册的方法:
/**
* 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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
通过SubscriberMethodFinder该类的findSubscriberMethods方法,查询到SubscriberMethod列表,SubscriberMethod类的构造方法:
public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
可知,五个参数为方法、事件类型、线程模式、优先级、sticky,SubscriberMethod的其他方法:
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof SubscriberMethod) {
checkMethodString();
SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
otherSubscriberMethod.checkMethodString();
// Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
return methodString.equals(otherSubscriberMethod.methodString);
} else {
return false;
}
}
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();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
现在清楚了,其实就是查询SubscriberMethodFinder类的作用是在Subscriber中找到所有以methodName(即默认的onEvent)开头的方法,具体我们可以看下findSubscriberMethods方法的代码。
EventBus中 findSubscriberMethods方法 2.4源码:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
synchronized (methodCache) {
subscriberMethods = methodCache.get(key);
}
if (subscriberMethods != null) {
return subscriberMethods;
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
// Skip system classes, this just degrades performance
break;
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}
}
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
现在应该豁然开朗,就是2.4版本,必须使用三种方法接收post过来的消息:MainThread、BackgroundThread、Async。而在3.0中的源码则是这样的:
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;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
subscriberMethods = findUsingReflection(subscriberClass);说的是利用反射来查询方法。
所以我们3.0通过注解,可以使用自定义的方法,不在受限,看完了注册的代码,
接下来就看下post的源码。
/** 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 = Looper.getMainLooper() == Looper.myLooper();
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;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
发现操作的对象是PostingThreadState,然后就是这段代码:
postSingleEvent(eventQueue.remove(0), postingState);
- 1
继续查看相关的代码:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
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));
}
}
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
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;
}
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 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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
这里就用到ThreadMode了:
如果是PostThread,直接执行
如果是MainThread,判断当前线程,如果本来就是UI线程就直接执行,否则加入mainThreadPoster队列
如果是后台线程,如果当前是UI线程,加入backgroundPoster队列,否则直接执行
如果是Async,加入asyncPoster队列。关于post,就暂时写到这里啊,主要是理清内部的处理机制。
另外还有优先级和Stick的使用。这里就不阐述了。EventBus关键的部分源码我就写到这里,以下写下我们如何在代码中简单使用该模块。
摘自:
https://blog.csdn.net/Alexlee1986/article/details/89242670
Android EventBus 3.X.X使用总结
EventBus 简介
EventBus 是Android 发布/订阅事件总线,可简化 Activities、Fragments、Threads、Services 等组件间的消息传递。EventBus使用了发布者/订阅者模式,其原理图如下:
优势:
- 可替代 Intent、Handler、BroadCast、接口等传统方案;
- 分离了事件的发送者和接受者;
- 避免了复杂的和易错的依赖关系和生命周期问题;
- 更快,代码更小,50K 左右的 jar 包;
- 代码更优雅,彻底解耦。
github地址:https://github.com/greenrobot/EventBus
添加依赖
在module的build.gredle 文件中的dependencies标签中添加
implementation 'org.greenrobot:eventbus:3.X.X'
- 1
PS:目前最新版本是3.1.1
三要素
Event:事件,它可以是任意类型。
Subscriber:事件订阅者。在EventBus3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe(),并且指定线程模型,默认是POSTING。
Publisher:事件的发布者。我们可以在任意线程里发布事件,一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
EventBus的基本用法
1.注册事件
EventBus.getDefault().register(this);
2.取消注册
EventBus.getDefault().unregister(this);
3.发送事件
EventBus.getDefault().post( “我发射了”);
EventBus.getDefault().postSticky(new updateTextEvent(“这是一个测试”)); //粘性事件: poststicky 事件:指在事件发布之后才注册的事件消费者也能接收到该事件的特殊类型,如切换新页面时,新页面的注册比旧页面的发送事件晚。EventBus3.0版本新增方法。
4.订阅处理事件:
处理消息的方法名字可以随便取。但是需要加一个注解@Subscribe,并且要指定线程模型。
/**
* 自定义一个方法 hello() ,用来接收事件。
* 方法名字可以随便写
* @return
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void hello (String event){
/* Do something */
Toast.makeText(this, event, Toast.LENGTH_SHORT).show();
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5.自定义事件用法
自定义事件:
public class MessageEvent{ private String message; public MessageEvent(String message){ this.message=message; } public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
注册和取消注册事件用法与普通事件相同;
发送事件:
MessageEvent messageEvent = new MessageEvent();
messageEvent.setMessage("自定义事件")
EventBus.getDefault().post(messageEvent);
- 1
- 2
- 3
处理事件:
@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(MessageEvent messageEvent) {
...
}
- 1
- 2
- 3
- 4
四种线程模型
在接收事件消息的方法中,可以通过注解的方式设置线程模型,EventBus内置了4中线程模型,分别是ThreadMode.POSTING、ThreadMode.MAIN、ThreadMode.BACKGROUND、ThreadMode.ASYNC。如订阅事时,采用添加注解的方法@Subscribe(threadMode = ThreadMode.MAIN)对事件的处理进行区别。
ThreadMode: POSTING
默认的线程模式,如果使用事件处理函数指定了线程模型为POSTING,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。此外,在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
ThreadMode: MAIN
事件的处理会在UI线程中执行。此外也不能在处理事件中执行比较耗时的操作,否则也会引起ANR。
ThreadMode: BACKGROUND
如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
ThreadMode: ASYNC
无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作,因为这牵涉到UI的更新只能在 main thread中更新。
EventBus 如何实现线程转换的
EventBus 中生产者和消费者模式的实现主要是在 PendingPostQueue里面。
PendingPostQueue 的实现比较简单,主要是通过在 enqueue 和 poll 的时候进行 synchronized 同步来实现的。
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
// 将 Post 插入到队列尾部
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
// 这里需要注意的地方是 PendingPost, 这里维护了一个 pendingPostPool 的池子, 当PendingPost 不再需要的时候,就释放回池子里面去,避免了新建对象的开销。
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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
EventBus 发布事件
每个ThreadModel (除了PostThread) 都维护了一个 Poster,这个Post里面维持了一个“生产者消费者模式”,来消费和使用事件。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
// 主线程的poster
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
优势对比
EventBus
EventBus的优势在于调度灵活。不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递,也解除了Handler所带来的耦合,父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要;通知的优先级,能够保证 Subscriber 关注最重要的通知;粘滞事件(sticky events)能够保证通知不会因 Subscriber 的不在场而忽略。可继承、优先级、粘滞,是 EventBus 比之于广播、观察者等方式最大的优点,它们使得创建结构良好组织紧密的通知系统成为可能。
缺点也很明显,EventBus中的事件分发是通过注解函数的参数类型决定的,这就导致了当接受者过多或相同参数时很难理清消息流。
BroadCast
广播是相对消耗时间、空间最多的一种方式。它是四大组件之一,许多系统级的事件都是通过广播来通知的,比如:电量的变化、网络的变化、短信的接收和发生状态等。
优点:与sdk连接紧密,当需要与Android交互时非常方便。而且可以实现跨进程通讯。必要时还能启动Activity
缺点:资源占用较多,且需要依赖Context
Handler
handler一般用于线程间通讯。handler的定义类和内部类是绑定的,这就造成了事件发布者和接受者之间的高耦合。使用handler最明显的优点是发生问题时,可以非常明确、快速的进行定位。