写在前面:EventBus是在项目中用到的一个很棒的开源事件总线的框架.使用起来方便简单,封装完善,不过笔者大部分(或者说是基本上)属于拿来主义,直接实用型,很少关注底层的一些封装思想等.写这篇文章也是为了总结EventBus 3.0的一些基本使用,方便以后查看,相当于学习笔记.
在这篇文章中将有一下内容:
- EventBus 的基本使用
- 源码分析
官网: http://greenrobot.org/eventbus/
GitHub: http://greenrobot.org/eventbus/
EventBus is an open-source library for Android using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.
大体意思就是说EventBus是一个基于发布者/订阅者用于解耦的开源类库,加快开发速度,使用简单.
大概的作用就是:
- 简化组建之间的通信
- 降低发送者和接受者之间的耦合
- 很好的转换activity Fragment 异步线程中的通信 (个人认为这个是最重要的)
- 使用简单
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
}
public interface Events{
class Evnet1{}
class Event2{}
//....
// 这里用接口是为了方便把所有的事件放到一起管理起来,个人习惯问题.当然也可以创建一个包专门用来存放事件类
}
public EventAcitvity extends Activity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(Events.Evnet1 event) {
/*
类似与handler.handlerMessage
通常都是在子线程中发送一个消息到handler中
这里EventBus也是这样
@Subscribe(threadMode = ThreadMode.MAIN)
这个注解代表的意思就是在主线程中执行下面这段代码方法名是随便定义的 参数就是接受你要发送的那个事件类
*/
};
EventBus.getDefault().post(new Events.Evnet1());
//这段代码通常是写在异步线程中.如请求网络后需要把新数据更新到界面
//当然你也可以把注册写到Service中.通过EventBus来解决Activity和Service之间的通信问题
这样看起来使用还是很简单的.通常情况下以上的写法基本上能解决大部分的使用场景了.异步线程请求网络,发送事件,通知主线程更新UI,替换Handler的一些使用场景等.当然还有其他的一些.
也就是在接受事件的方法中,使用过EventBus之前版本的童鞋们应该会有体会.
老版本中接受方法的方法名后面是固定的.在v3.0中是可以随意的自定义,只需要在方法上加入
@Subscribe
对设计模式有过了解的童鞋们可能看到这个就想到了观察者模式,在这里笔者想说的是,就个人理解来看,这东西就是观察者模式的的一种运用方式.对发送出来的的事件进行订阅,简单来看一下他的源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
// 线程模式.默认是发送事件的线程
ThreadMode threadMode() default ThreadMode.POSTING;
//粘性
boolean sticky() default false;
//优先级,优先级越高,在调用的时候会越先调用。
int priority() default 0;
}
看起来很简洁.不过核心的是 ThreadMode 这个类中定义的4个常量.它是一个美枚举类,如下
public enum ThreadMode {
//订阅方法和发送者在同一个线程中执行,直白的来说就是,如果你调用
第五步发送事件的方法是在子线程中执行的,那么定义的接受方法也是在子线程中执行的.
//这样的话就不能用来直接更新UI了,否则会出现异常
POSTING,
// 切换到UI线程,这个很明显
MAIN,
/**
* 如果在非UI线程发布的事件,则直接执行,和发布在同一个线程中。如果在UI线程发布的 事件,则加入后台任务队列,使用线程池一个接一个调用。
*/
BACKGROUND,
//加入后台任务队列,使用线程池调用,注意没有BackgroundThread中的一个接一个
ASYNC
}
注释中写的已经很明白了.这里不在赘述,具体的使用场景还是需要根据业务来判断使用那种模式.
EventBus.getDefault();
/** 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.getDefault().register(this);
public class EventBus {
//这里截取核心代码
....
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder
.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
注册方法中传入的this代表的是那个类,方法的参数类型是一个Object,可以看出来,任何类型都可通过这个方法注册,而且方法中第一样也表明了.需要的是这个Class对象,与旧版EventBus不同的是,这里只有这一个register方法,取消了优先级,粘性方法的重载,感兴趣的童鞋们可以看看旧版的源码就知道了.
EventBus 本身是使用了建造者模式,也就是通常的Builder.build();形式
EventBus 本身有了一套默认的参数设置,如下
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
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 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> skipMethodVerificationForClasses;
List subscriberInfoIndexes;
EventBusBuilder() {
}
}
主要是一些默认的参数设置,不过多描述.需要注意的是这里的这个默认的线程池DEFAULT_EXECUTOR_SERVICE,它是一个只有非核心线程的线程池,最大线程数为Integer.MAX_VALUE,因为这是一个非常大的一个数量,可以把他默认为无限大,线程超时的时间为60s,超过了会回收限制线程,感兴趣的童鞋可以深入了解一下.
这里需要注意的是SubscriberMethodFinder这个类,如下
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;
}
}
}
private List 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 {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
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");
}
}
}
这里就是EventBus 的真正核心了 METHOD_CACHE这个一定要记住,非常重要!!!
它本身是一个map的,主要用途简单来说就是把注册的类中所有的方法都遍历一边,通过反射,寻找到具有 @Subscribe注解的方法,获取相关的参数获取后存转换成SubscriberMethod放到List中,在以这个类本身当成key存有相关方法的List作为value存放到METHOD_CACHE中,相当于一个内存缓存. 上面代码片段总 6 ~ 8 行中也充分的说明了这个,首先通过传递过来的参数获取这个类对应的说有被 @Subscribe注释的方法集合,有的话直接返回,没有的话在进行进一步的处理.
由于默认的参数都是为 true [EventBusBuilder]中的设置,忘记了回去看一下,说以直接调用findUsingReflection 进行方法的处理,获取到返回结果后,放入到METHOD_CACHE 直接返回处理过后的方法集合
findUsingReflectionInSingleClass这个方法中是对被注解类的方法进行反射遍历处理等.
EventBus.getDefault().unregister(this);
/** 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 {
Log.w(TAG, "Subscriber to unregister was not registered before: "
+ subscriber.getClass());
}
}
取消注册,基本上就是属于遍历,移除之类的.没什么好解释的.
EventBus.getDefault().post(new Events.Evnet1());
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
大概就是register 时候是把方法存放好,等待调用.那么Post基本上就是重中取到相对应的方法,然后调用
PostingThreadState 是保存在ThreadLoacl中的参数.主要是为了区别是哪一个线程中调用或者保存的,不严谨的说ThreadLoacl就是分别为多个线程保存那个线程中存在的状态或者数据的.比如我有3个线程
ThreadLoacl local = new ThreadLoacl();
new Thread(new Runnable() {
@Override
public void run() {
local.put("key","true")
}
},"Thrad1").start();
new Thread(new Runnable() {
@Override
public void run() {
local.put("key","false")
}
},"Thrad2").start();
new Thread(new Runnable() {
@Override
public void run() {
local.put("key","true")
}
},"Thrad3").start();
然后分别打印结果,分别是true false true 他们之间是互相不影响的,具体不详细描述了,感兴趣的话可以去搜索看看.
然后在判断当前的线程,这个事件是否在发送等等. 剩下的就交给了postSingleEvent方法
这个方法中大概就是属于根据你传入的事件类去进行匹配. 去register中的那个METHOD_CACHE中匹配,然后还是通过反射执行对应的方法
postToSubscription方法中是进行线程的判断 ThreadMode中的那4个类型
case MAIN:
首先去判断当前如果是UI线程,则直接调用;否则: mainThreadPoster.enqueue(subscription, event);把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,然后执行的。
case BACKGROUND:
如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用
executorService = Executors.newCachedThreadPool();。
case ASYNC:
将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。
到这里.基本上就解析的差不多了,当然还有很多细节问题没有详细说明,但这篇文章相当于分析的总结之做,写的时候又重新过了一边.加深了一下印象,希望忘记的不会那么快,同时也是想到那个做了一次笔记.重要的是还需要 RTFS 多么xxx 的一句话啊..
Android EventBus源码解析带你深入理解EventBus 需要说明一下的是这个版本比较老.实现和使用的细节可能有差别,但是具体流程,思想核心基本上不变.对我的学习分析有很大帮助,在此感谢作者带来这么详细的分析.