首先,简单介绍一下EventBus,这是一个事件的发布与订阅框架,大大的简化了android中事件传递。
如:在fragment中任何地方都可以向activity传递事件和值,可以替代Handler、BroadCastReceiver、Interface 回调等传值方式。同时与这些方式传递事件相比,EventBus优点在于,使用简单,方便,易操作。
接下来,介绍一下EventBus的使用:
第一步:定义一个事件类:
public static class ClassEvent{
public String data;
ClassEvent( String data){
this.data=data;
}
}
第二步:任一类中进行订阅,取消订阅事件。
一般在Activity或Fragment的onResume中,进行订阅事件。
EventBus.getDefault().register(ThirdActivity.this);
一般在Activity或Fragment的onDestroy中,进行取消订阅事件。
EventBus.getDefault().unregister(ThirdActivity.this);
第三步,定义出接收事件的方法
@Subscribe(threadMode = MAIN)
public void EventBus(ClassEvent event){
tv_result.setText(event.jsonData);
}
第四步:在任意地方发送事件。
EventBus.getDefault().post(new ClassEvent(“某个地方发送过来的数据”));
接下来,看是看源码。
官方EventBus给了一个简单的定义:
EventBus is a publish/subscribe event bus optimized for Android.//EventBus 是一个android最佳的事件分发和订阅的框架。
这个示意图简单明了的介绍了EventBus的原理。
发布者Publisher通过post一个Event事件,然后有一个或多个订阅者Subscriber来接收这个Eevent事件。
源码解析:
EventBus.getDefault();
这个就不用解释了,获取EventBus对象,用到了单例模式和同步锁,保证安全性。
EventBus的构造方法,初始化了一些属性,
subscriptionsByEventType:订阅该事件的所有订阅者 。是一个以 key:订阅的事件 value:订阅这个事件的所有订阅者。因此当post的时候会遍历这个Map相应对应事件的List。
typesBySubscriber:订阅者中的所有订阅事件 。是一个以key:订阅者 value:订阅者中的所有订阅事件。用于后续的取消订阅。
三个Poster类:
mainThreadPoster,backgroundPoster,asyncPoster用来处理sticky粘性事件。
EventBus.getDefault().register():register方法:
/**
* 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);
}
}
}
参数传入一个订阅者对象subscriber,然后通过反射subscriber.getClass()获取订阅者类
SubscriberMethodFinder查找出订阅者中的所有订阅事件方法。进入findSubscriberMethods方法看看:
返回结果为所有订阅事件的方法List
该方法的流程如下:
我们分析一下通过findUsingReflection 方法,通过反射获取订阅者中的所有订阅事件方法。进去看看这个方法:
查找到的订阅事件会在临时的类FindState中做校验和保存,为什么会提到这个临时类呢?这里有一个很好的设计,使用了“对象池”的概念来创建FindState对象,和线程池同一个概念, 这样可以使对象FindState复用,防止创建过多的对象,增加内存开销。
去看看如何复用FindState对象的,进入上图prepareFindState方法:
在112中 可以看到 会从对象池FIND_STATE_POOL取出FindState 而不是每次都创建对象。
回到 findUsingReflection方法当查找完所有的订阅事件方法,调用getMethodsAndRelease对FindState进行回收复用:
还是回到 findUsingReflection 方法,调用了findUsingReflectionInSingleClass 方法来进行查找订阅事件方法,进去看看是怎么查找的:
在154行 通过反射 获取订阅者(this)的所有方法。
在160 行遍历这些方法。
在164行 判断是否带@Subscribe 注解的方法 是否只有一个参数,如果否 就会抛出异常
在165行判断方法是否带@Subscribe 注解 ,如果是,通过FindState对象进行校验和保存。
其实很简单 就是通过反射来查找到订阅者的所有方法,查找出带@Subscribe注解的方法保存起来。
回到register方法,查找完所有的订阅者中的所有订阅事件方法,之后遍历列表,进入subscribe方法:
在146行 获取订阅方法的订阅类型(就是带@Subscribe 注解的方法的第一个参数)。
封装Subscription对象,保存到subscriptionsByEventType数据集合中。
在159行,根据优先级 把封装Subscription对象,保存到typesBySubscriber数据集合中。
在174行 如果是sticky事件,立即post sticky事件到当前订阅者。
整个register的流程大概就是这样:
通过post方法 来发送事件,进入post方法
首先从currentPostingThreadState 获取 PostingThreadState 对象
currentPostingThreadState 是个ThreadLocal对象
private final ThreadLocal currentPostingThreadState = new ThreadLocal() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
PostingThreadState postingState = currentPostingThreadState.get();
ThreadLocal使得各线程能够保持各自独立的一个对象。
把要post的事件加入PostingThreadState 的eventQueue队列中,循环取出事件。
进入postSingleEvent方法
在365行,判断是否触发订阅该事件的父类,接口的方法。
调用postSingleEventForEventType 方法分发事件。
进入postSingleEventForEventType方法
在389行,从subscriptionsByEventType数据集合中取出该订阅事件的所有订阅者。
在392行,分发所有的订阅者,通过调用postToSubscription方法。
进入postToSubscription方法
根据不同的threadMode 在不同的线程中处理,最终都会调用invokeSubscriber方法,把事件invoke到订阅者的方法中。
ThreadMode 共有四类:
PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作;
MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread
类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;
BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。
unregister:
从 typesBySubscriber 数据集合中取出该订阅者的所有订阅方法,remove所有订阅者的订阅方法,防止内存泄漏。
整个post流程:
EventBus 属于一个比较容易理解的开源库,项目整体的框架设计采用了观察者模式,但不论从使用方式和实现方式上都是非常值得我们学习的开源项目。比如,对象池的设计,三大Poster类的设计,多用组合,少用继承,缓存的设计等都值得我们借鉴和使用。
本篇文章学习自 https://juejin.im/entry/5857d31b61ff4b00686cb307