概述
是一个发布/订阅事件。可以用来进行组件之间的通信,比如Activity与Activity,Activity与Fragment,Thread,Services通信。 常用的通信有
- Handler
- BroadcastReceiver/LocalBroadcastReceiver。
- 接口回调
- 文件I/O
- Sharepreference。
那么使用EventBus好处就是解藕,更优雅来实现通信,下面一起来分析EventBus的原理机制。 框架源码:github.com/greenrobot/… 通过链接下载
具体流程
- 注册订阅者
- 发布事件
- 响应事件方法
- 取消注册订阅者
在使用订阅之前通过build.gradle文件中添加依赖
dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
//添加EventBus 依赖
compile 'org.greenrobot:eventbus:3.1.1'
}
复制代码
一.订阅
在Activity,或者Fragment,在onCreate()方法通过 EventBus.getDefault().register(this)完成注册 ,声明订阅事件onEvent和事件类型NormalMessage,onEvent和NormalMessageEventType可以自己定义,用来发布的时候来识别, 但注意的是必须是要声明@Subscribe和这个订阅者响应方法是public,方法里必须有参数,也就是事件类型
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册订阅者
EventBus.getDefault().register(this);
}
/**
* 订阅者方法
* @type {[type]}
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(NormalMessageEventType normalMessage){
Log.i("test","onEvent");
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册订阅者
EventBus.getDefault().unregister(this);
}
}
复制代码
二.发布
通过 EventBus.getDefault().post(new NormalMessageEventType())来发布
/**
* Created by longer on 2018/8/30.
*/
public class TwoActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
initView();
}
private void initView() {
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new NormalMessageEventType());
}
});
}
}
复制代码
三.响应事件
也就是刚才订阅onEvent事件,通过NormalMessage事件类型来获取响应,在不同的Activity,或Fragment有多个NormalMessage事件类型也是会响应的,因为EventBus是通过事件类型来循环事件调用的,具体下面会源码分析
public class MainActivity extends FragmentActivity {
/**
* 订阅者方法
* @type {[type]}
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(NormalMessage normalMessage){
Log.i("test","onEvent");
}
}
复制代码
四.取消注册的订阅者
在Activity,Fragment的onDestroy()里取消注册,官网是在onStop()但会有问题
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册订阅者
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册订阅者
EventBus.getDefault().unregister(this);
}
}
复制代码
好了下面就开始进行源码分析吧。
源码解析
注册订阅者
先通过注册入口 EventBus.getDefault().register(this);着手
public class EventBus {
/** Log tag, apps may override it. */
public static String TAG = "EventBus";
static volatile EventBus defaultInstance;
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
//
private static final Map, List>> eventTypesCache = new HashMap<>();
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
//记录所有订阅者,用来取消注册使用
private final Map
EventBus是一个懒汉模式双重检索的单例,通过构造方法初始化成员变量,具体来看
1.Map
关于Poster,BackgroundPoster,AsyncPoster我们到分析发布订阅事件的时候再分析,好继续往下看,
发现EventBus.getDefault().register(this)来注册的时候实际是先获取订阅者的Class信息,也就是Activity,或者Fragment等Class订阅者的类消息,再使用SubscriberMethodFinder类里的findSubscriberMethods获取订阅的响应事件方法 ,那他具体是怎么去获取的?具体进一步看看
class SubscriberMethodFinder {
private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
List findSubscriberMethods(Class> subscriberClass) {
//获取订阅者缓存,如果有直接返回,没有先判断是否忽略生成下标
List 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;
}
}
}
复制代码
我们先忽略ignoreGeneratedIndex,因为这个会默认显示false
class SubscriberMethodFinder {
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//最终会进入
//
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");
}
}
}
}
复制代码
发现调用findUsingInfo(Class> subscriberClass)方法,其他的不用管他,最终会进入findUsingReflectionInSingleClass(FindState findState)反射,然后获取@Subscribe注解,获取方法,用方法第一个参数获取事件类型,再封装SubscriberMethod,这下就真相大白了
先有必要理解下SubscriberMethod这个类是干啥的,在注册订阅的时候进行循环遍历的就是SubscriberMethod来订阅
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
//查找订阅者的所有订阅响应的事件方法,
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
复制代码
package org.greenrobot.eventbus;
public class SubscriberMethod {
final java.lang.reflect.Method method;
final org.greenrobot.eventbus.ThreadMode threadMode;
final java.lang.Class> eventType;
final int priority;
final boolean sticky;
java.lang.String methodString;
public SubscriberMethod(java.lang.reflect.Method method, java.lang.Class> eventType, org.greenrobot.eventbus.ThreadMode threadMode, int priority, boolean sticky) { /* compiled code */ }
public boolean equals(java.lang.Object other) { /* compiled code */ }
private synchronized void checkMethodString() { /* compiled code */ }
public int hashCode() { /* compiled code */ }
}
复制代码
其实仔细看SubscriberMethod 就是自己通过@Subscribe定义事件(订阅响应的事件),里面包含 1.method(Java反射的方法信息) 2.threadMode(线程模型), 下面会介绍到 3.eventType 事件类型 4.priority 优先级 5.sticky 是否为粘性事件
这次,已经分析EvenBus,SubscriberMethod,通过反射来找到订阅者的事件,下面分析如何订阅
如何去订阅?
上面知道是通过遍历SubscriberMethod再执行subscribe()方法,具体来看看做了什么
public class EventBus {
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map
分三步来看
- 通过Subscription 类把subscriber,subscriberMethod封装
- 通过订阅者的事件类型eventType来作为value,CopyOnWriteArrayList保存所有的订阅,如果有相同事件类型,就直接copyOnWriteArrayList集合中add,否则创建一个CopyOnWriteArrayList,最终 subscriptionsByEventType.put(eventType, subscriptions);把事件类型映射的所有订阅关联
- 把相关订阅者的事件类型加入到typesBySubscriber集合中,这个集合是用来取消注册的时候用到
- 最后判断是否为粘性,如果为粘性直接回掉
如何订阅分析到这里。
如何发布订阅?
EventBus.getDefault().post(new NormalMessageEventType()); NormalMessageEventType这个类是自己定义的事件类型。
public class EventBus {
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map
这里我们只要关注post()和postToSubscription()两个方法就好了,其他不用太关心
post()做了些啥,无疑是 1.PostingThreadState类构建了一个事件队列,而这个队列是用List表示的 2.如果用Event就加入队列中 3.如果是未发布状态就进入while()循环时间队列
在来看下PostingThreadState类,还是很容易理解的吧,静态内部类
public class EventBus {
private final ThreadLocal currentPostingThreadState = new ThreadLocal() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
}
复制代码
再进入到postToSubscription(),在switch中有四种线程模型,也就是在响应事件的方法是处于什么线程,重点要知道下
- POSTING 同一个线程,直接调用,默认, 不可做耗时操作
- MAIN 主线程,不可做耗时操作,要不然会ANR
- BACKGROUND 后台线程,不可做耗时操作,要不然会ANR
- ASYNC 异步线程,可做耗时操作
一、先分析Main
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
}
}
复制代码
如果线程模型是Main,再判断isMainThread,这个变量MainThreadSupport接口中的isMainThread(),实际是主线程的Lopper是否已经复制,而在构建EventBus类时在构造函数就赋值好了, 所以直接入队
/**
* Interface to the "main" thread, which can be whatever you like. Typically on Android, Android's main thread is used.
*/
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper, 10);
}
}
}
复制代码
/**
* Posts events.
*
* @author William Ferguson
*/
interface Poster {
/**
* Enqueue an event to be posted for a particular subscription.
*
* @param subscription Subscription which will receive the event.
* @param event Event that will be posted to subscribers.
*/
void enqueue(Subscription subscription, Object event);
}
复制代码
是个接口,里面包含了Poster ,AndroidHandlerMainThreadSupport 具体实现,isMainThread()实际是获取主线程的Looper对象,再看下mainThreadPoster 是如何实现的
public class EventBus {
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;
}
}
复制代码
也就是说,通过createPoster来创建Poster,Poster实例里面是一个new HandlerPoster 对象,再来看看
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;
}
}
}
复制代码
final class PendingPost {
private final static List pendingPostPool = new ArrayList();
Object event;
Subscription subscription;
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
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);
}
}
}
}
复制代码
哈哈,这是Handler,只是实现了一个Poster接口,然后通过enqueue()事件入队的方式加入,这里要注意的是PendingPost 是一个先进先出双向链表,PendingPost类里面有一个event事件,Subscription 订阅的引用 。 继续,HandlerPoster 类里的handleMessage()就很好理解了,用handler 主线程的Looper来进行工作,没看到Handler的原理可以看下,简单说就是 1.enqueue 入队,sendMessage(obtainMessage())发送消息 2.进入handleMessage(),循环队列的事件消息,每次先从队头poll()探出事件,再通过EventBus调用;
分析完毕。
二、BACKGROUND(后台线程)
public class EventBus {
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
}
}
}
复制代码
后台线程,和上面的一样isMainThread=true,直接进入enqueue,BackgroundPoster类做了啥
final class BackgroundPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
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;
}
}
}
复制代码
发现了,是一个Runnable接口,再调用enqueue加入到PendingPost链表中,然后用线程池执行,接着进入run()方法,也是一个循环来调用,直接PendingPost 链表为null,结束
三、ASYNC(异步)
class EventBus{
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
}
}
}
复制代码
直接进入AsyncPoter类中的enqueue方法
/**
* Posts events in background.
*
* @author Markus
*/
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);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
复制代码
这里和后台线程一样,实现Ruanable接口,入队,加入线程池,调用run()方法,不同的是 run()方法是直接弹出队列,是非阻塞的,可以做耗时操作。
最后一步,
如何取消注册
public class EventBus {
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map>> typesBySubscriber;
private final Map, Object> stickyEvents;
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List> 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());
}
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
List 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--;
}
}
}
}
}
复制代码
这个就很简单了,typesBySubscriber 订阅者集合中删除。
到此源码都分析完毕。如果有问题的可直接留言。