EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息。
它是一个基于观察者模式的事件发布/订阅框架,开发者可以通过极少的代码去实现多个模块之间的通信,而不需要以层层传递接口的形式去单独构建通信桥梁。从而降低因多重回调导致的模块间强耦合,同时避免产生大量内部类。它拥有使用方便,性能高,接入成本低和支持多线程的优点,实乃模块解耦、代码重构必备良药。
EventBus作为一个事件总线,有四个主要元素:发布者(Publisher),订阅者(Subscriber),事件(Event),总线(Bus)。
四者的关系:订阅者从总线订阅事件,发布者发布事件到总线。订阅者可以订阅多个事件,发布者可以发布任何事件,发布者同时也可以是订阅者。
打开App的build.gradle,在dependencies中添加最新的EventBus依赖:
compile 'org.greenrobot:eventbus:3.0.0'
这里我添加的是EventBus3.0版本,3.0和2.x是有一些区别的,具体见后面。
public class AnyEventType {
public AnyEventType(){}
}
EventBus.getDefault().register(this); //这里获取单例类来进行注册
EventBus.getDefault().post(new AnyEventType event);
public void onEvent(AnyEventType event) {}
public void onEventMainThread(AnyEventType event) {}
public void onEventBackgroundThread(AnyEventType event) {}
public void onEventAsync(AnyEventType event) {}
EventBus.getDefault().unregister(this);
我们的需求是这样的,两个Fragment组成主界面,左边的Fragment是个目录、即列表,右边的Fragment是详细信息面板;
a、目录的列表是从网络获取的。
b、当点击目录上的条目时,动态更新详细信息面板;
效果图:
有了这个需求,我们传统的做法是:
a、目录Fragment在onCreate中去开启线程去访问网络获取数据,获取完成以后,通过handler去更新界面。
b、在目录Fragment中提供一个接口,然后详细信息面板去注册这个接口,当发生点击时,去回调这个接口,让详细信息面板发生改变。
其实这种做法也还是不错的,但是有了EventBus之后,我们交互会发生什么样的变化呢?拭目以待吧。
(1)MainActivity及其布局
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" android:divider="?android:attr/dividerHorizontal" android:orientation="horizontal" android:showDividers="middle">
<fragment android:id="@+id/item_list" android:name="com.hx.eventbus.ItemListFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" />
<fragment android:id="@+id/item_detail_container" android:name="com.hx.eventbus.ItemDetailFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="2" />
</LinearLayout>
可以看到,我们MainActvity可以说没有一行代码,布局文件即两个Fragment组成;
(2)自定义Event类,这里我们定义两个Event,一个是点击左侧列表栏发布事件的Item类,一个是从网络获取列表栏内容发布事件的ItemListEvent类。
public class Item {
public String id;
public String content;
public Item(String id, String content) {
this.id = id;
this.content = content;
}
@Override
public String toString() {
return content;
}
}
public class ItemListEvent {
private List<Item> items;
public ItemListEvent(List<Item> items) {
this.items = items;
}
public List<Item> getItems() {
return items;
}
}
(3)ItemListFragment
public class ItemListFragment extends ListFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//事件的订阅
EventBus.getDefault().register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
//事件的取消
EventBus.getDefault().unregister(this);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//开启线程加载列表
new Thread() {
public void run() {
try {
Thread.sleep(2000); //模拟延时
//发布事件,在后台线程发的事件
EventBus.getDefault().post(new ItemListEvent(getItemList()));
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}
private List<Item> getItemList() {
List<Item> ITEMS = new ArrayList<Item>();
// Add 6 sample items.
ITEMS.add(new Item("1", "Item 1"));
ITEMS.add(new Item("2", "Item 2"));
ITEMS.add(new Item("3", "Item 3"));
ITEMS.add(new Item("4", "Item 4"));
ITEMS.add(new Item("5", "Item 5"));
ITEMS.add(new Item("6", "Item 6"));
return ITEMS;
}
//接收消息,主线程中进行
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(ItemListEvent event) {
setListAdapter(new ArrayAdapter<Item>(getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1, event.getItems()));
}
//左侧列表的点击事件
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
//发布事件,在ItemDetailFragment接收
EventBus.getDefault().post(getListView().getItemAtPosition(position));
}
}
ItemListFragment里面在onCreate里面进行了事件的订阅,onDestroy里面进行了事件的取消;onViewCreated中我们模拟了一个子线程去网络加载数据,获取成功后我们调用
了EventBus.getDefault().post(new ItemListEvent(getItemList()));发布了一个事件。
现在来看看事件的接收,如果方法名以onEvent开头,则代表要接收一个已订阅的事件,这里当上面事件发布的时候,onEventMainThread就会自动被执行,且是在UI线程中,可以看到这里事件的传递已经跨线程了。
那么发布和接收两者的关联关系是什么呢?
其实是参数的类型,我们onEventMainThread需要接收一个ItemListEvent ,我们也发布了一个ItemListEvent的实例,于是接收函数onEventMainThread就自动执行了。其实我们还可以定义多个接收函数(如onEvent、onEventBackgroundThread等),当有符合条件的事件被发布,它们都会被执行。
其中的原理是什么呢?
在onCreate里面执行EventBus.getDefault().register(this);意思是让EventBus扫描当前类,把所有onEvent开头的方法记录下来,如何记录呢?使用Map,Key为方法的参数类型,Value中包含我们的方法。这样在onCreate执行完成以后,我们的onEventMainThread就已经以键值对的方式被存储到EventBus中了。
然后当子线程执行完毕,调用EventBus.getDefault().post(new ItemListEvent(getItemList()))时,EventBus会根据post中实参的类型,去Map中查找对应的方法,于是找到了我们的onEventMainThread,最终调用反射去执行我们的方法。
现在你应该明白了,整个运行的流程了,所以没有接口却能发生回调应该也能解释了。
继续往下看代码,onListItemClick则是ListView的点击事件,我们调用了EventBus.getDefault().post(getListView().getItemAtPosition(position));去发布一个事件,getListView().getItemAtPosition(position)的类型为Item类型。这个事件的接收其实在ItemDetailFragment当中,这里事件的传递已经跨Fragment了。
(4)ItemDetailFragment
public class ItemDetailFragment extends Fragment {
private TextView tvDetail;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
//List点击时发布事件,接收到事件后更新详情
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(Item item) {
if (item != null)
tvDetail.setText(item.content);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_item_detail, container, false);
tvDetail = (TextView) rootView.findViewById(R.id.text);
return rootView;
}
}
这个Fragment的流程就是:onCreate时,EventBus扫描当前类,将onEventMainThread以键值对的形式进行存储,键为Item.class ,值为包含该方法的对象。
然后当ItemListFragment中Item被点击时,发布了一个事件:EventBus.getDefault().post(getListView().getItemAtPosition(position));实参的类型恰好是Item,于是触发我们的onEventMainThread方法,并把Item实参传递进来,我们更新控件。
EventBus包含4个ThreadMode:PostThread,MainThread,BackgroundThread,Async。
分别对应四种订阅函数:
public void onEvent(AnyEventType event) {}
public void onEventMainThread(AnyEventType event) {}
public void onEventBackgroundThread(AnyEventType event) {}
public void onEventAsync(AnyEventType event) {}
这四种订阅函数都是使用onEvent开头的,它们的功能稍有不同:
onEvent
如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
onEventMainThread
如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中更新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
onEventBackground
如果使用onEventBackgrond作为订阅函数,那么如果事件是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。而如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,加入后台任务队列,使用线程池一个接一个调用。
onEventAsync
使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync,注意没有BackgroundThread中的一个接一个。
涉及到UI操作必须要在主线程中进行,在EventBus2.x时,直接在onEventMainThread方法内就可以执行UI操作,而到3.0时做了一些修改,只要在上面任一方法上加上@Subscribe(threadMode = ThreadMode.MAIN)注解就行,表明是在UI线程中操作。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(AnyEventType event) {}
也就是说,2.x中接收函数的处理线程主要看函数名,而3.0以后则主要看注解@Subscribe(threadMode = ThreadMode.XXX)。
ThreadMode还提供了以下几种Mode,对应上面四个接收函数,视情况选择。
@Subscribe(threadMode = ThreadMode.POSTING)
@Subscribe(threadMode = ThreadMode.MAIN)
@Subscribe(threadMode = ThreadMode.BACKGROUND)
@Subscribe(threadMode = ThreadMode.ASYNC)
另外值得注意的就是,原先的onEventMainThread等固定命名方法,在3.0下可以由用户任意定义,只需在方法上加上对应的注解@Subscribe即可,你可以写成下面的样子:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventXXX(AnyEventType event) {
String action = event.getAction();
...
}
注:如果我们定义了多个接收函数,当有符合条件的事件被发布时,它们会在各自的线程中分别被执行。
我们写EventBus都是从rigister开始,我们就从EventBus.getDefault().register(this);入手,首先查看EventBus.getDefault(),看看EventBus是如何初始化的:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可以看出来,EventBus是单例模式存在的,一个项目中只能有一个EventBus这样有利于管理订阅者和订阅方法,这会在下面的介绍中体现出来。接下来看register(this):
public void register(Object subscriber) {
//订阅者
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
可以看出首先获取订阅者的类对象Class<\?> subscriberClass = subscriber.getClass(); 在看这段代码之前,我们首先要了解SubscriberMethod和subscriberMethodFinder.findSubscriberMethods方法到底做了什么
首先来看SubscriberMethod:
public class SubscriberMethod {
final Method method;//方法
final ThreadMode threadMode;//执行线程
final Class<?> eventType;//接收的事件类型
final int priority;//优先级
final boolean sticky;
/** Used for efficient comparison */
String methodString;
....
}
可以看出SubscriberMethod其实就是一个订阅方法的实体类,里面保存了订阅方法信息,接着看subscriberMethodFinder.findSubscriberMethods,该方法的作用其实就是从订阅类中获取所有的订阅方法信息;
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//首先从缓存中读取
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注解器生成的MyEventBusIndex类
if (ignoreGeneratedIndex) {
//利用反射来获取订阅类中的订阅方法信息
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
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、通过EventBusAnnotationProcessor(注解处理器)生成的MyEventBusIndex中获取
2、利用反射来读取订阅类中订阅方法信息
在3.0版本中,EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析, 处理其中所包含的信息, 然后生成java类来保存所有订阅者关于订阅的信息, 这样就比在运行时使用反射来获得这些订阅者的信息速度要快。我们可以参考EventBus项目里的EventBusPerformance这个例子,编译后我们可以在build文件夹里找到这个类
,MyEventBusIndex 类,当然类名是可以自定义的。我们大致看一下生成的MyEventBusIndex类是什么样的:
/** * This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscriberClassEventBusAsync.class,
true, new SubscriberMethodInfo[]{
new SubscriberMethodInfo("onEventAsync", TestEvent.class, ThreadMode.ASYNC),
}));
putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[]{
new SubscriberMethodInfo("onEventMainThread", TestFinishedEvent.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
可以看出是使用一个静态HashMap即:SUBSCRIBER_INDEX来保存订阅类的信息,其中包括了订阅类的class对象,是否需要检查父类,以及订阅方法的信息SubscriberMethodInfo的数组,SubscriberMethodInfo中又保存了订阅方法的方法名,订阅的事件类型,触发线程,是否接收sticky事件以及优先级priority.这其中就保存了register()的所有需要的信息;
我们重点研究一下通过反射来获取订阅方法信息即:findUsingReflection(subscriberClass);
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
FindState其实就是一个里面保存了订阅者和订阅方法信息的一个实体类,包括订阅类中所有订阅的事件类型和所有的订阅方法等。我们看到会首先创建一个FindState对象并执行findUsingReflectionInSingleClass(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;
}
//遍历所有方法,忽略private类型的,最后如果是公有,并且不是
//java编译器 生成的方法名,那么就是我们要的了。
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");
}
}
}
可以看到,首先会得到订阅类的class对象并通过反射获取订阅类中的所有方法信息,然后通过筛选获取到订阅方法集合。程序执行到此我们就获取到了订阅类中的所有的订阅方法信息,接下来我们就要对订阅方法进行注册;
subscribe(subscriber, subscriberMethod);//参数:1订阅者2订阅方法集
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取订阅方法的参数类型
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根据订阅的事件类型获取所有的订阅者
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//将订阅者添加到subscriptionsByEventType集合中
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
}
//根据优先级,将订阅者插入到指定的位置
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//获取订阅者所有订阅的事件类型
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//将该事件类型添加到typesBySubscriber中
subscribedEvents.add(eventType);
//如果接收sticky事件,立即分发sticky事件
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
上面这段代码涉及到几个对象我来介绍一下:
Subscription //订阅者信息
final class Subscription {
final Object subscriber;//订阅者
final SubscriberMethod subscriberMethod;//订阅方法
}
subscriptionsByEventType
key订阅方法类型,values 所有订阅了该类型的订阅者集合
Map<\Class<\?>,CopyOnWriteArrayList<\Subscription>> subscriptionsByEventType;
typesBySubscriber
key订阅者,values订阅事件集合
Map<\Object, List<\Class<\?>>> typesBySubscriber;
了解了这几个对象,上面的代码就很容易看懂了:
1、首先获取订阅方法的参数类型即订阅事件类型
2、根据订阅事件类型获取该事件类型的所有订阅者
3、将该订阅者添加到该事件类型的订阅者集合中即:subscriptionsByEventType
4、获取订阅者所有的订阅事件类型
5、将该事件类型添加到该订阅者的订阅事件类型集中即:typesBySubscriber
至此,就完成了订阅类中订阅方法的注册,我们来看一下整个流程:
接下来我们来分析EventBus的事件发布机制即EventBus.getDefault().post(“…”);
我们从post方法入手:
/** Posts the given event to the event bus. */
public void post(Object event) {
//获取当前线程的postingState
PostingThreadState postingState = currentPostingThreadState.get();
//取得当前线程的事件队列
List<Object> eventQueue = postingState.eventQueue;
//将该事件添加到当前的事件队列中等待分发
eventQueue.add(event);
if (!postingState.isPosting) {
//判断是否是在主线程post
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;
}
}
}
什么是PostingThreadState?
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();//当前线程的事件队列
boolean isPosting;//是否有事件正在分发
boolean isMainThread;//post的线程是否是主线程
Subscription subscription;//订阅者
Object event;//订阅事件
boolean canceled;//是否取消
}
PostingThreadState中包含了当前线程的事件队列,就是当前线程所有分发的事件都保存在eventQueue事件队列中,以及订阅者订阅事件等信息,有了这些信息我们就可以从事件队列中取出事件分发给对应的订阅者。
PostingThreadState怎么获得?
//ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
可以看出currentPostingThreadState的实现是一个包含了PostingThreadState的ThreadLocal对象,这样可以保证取到的都是自己线程对应的数据。
我们有了PostingThreadState获取到了当前线程的事件队列,接下来就是事件分发,我们来看postSingleEvent(eventQueue.remove(0), postingState);
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//得到事件类型
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//是否触发订阅了该事件(eventClass)的父类,以及接口的类的响应方法.
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));
}
}
}
通过以上代码我们可以发现,真正的事件分发是通过postSingleEventForEventType(event, postingState, eventClass);发出去的我们来看:
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;
}
可以看到首先根据事件类型获取到所有的订阅者,然后循环向每个订阅者发送事件,通过postToSubscription(subscription, event, postingState.isMainThread);发送出去
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING://默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,
//不论该线程是否为主线程(UI 线程)。
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);
}
}
以上的四种threadMode可以看代码注释简单了解一下,通过一下代码我们来看一下订阅方法最后是通过invokeSubscriber(subscription, event);来执行的
//最终通过反射调用订阅者的订阅函数 并把event作为参数传入
void invokeSubscriber(Subscription subscription, Object event) {
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);
}
}
真相大白;最后是通过反射的方式,调用了订阅类中的订阅方法。我们来总结一下整个事件分发的过程
1、首先获取当前线程的PostingThreadState对象从而获取到当前线程的事件队列
2、通过事件类型获取到所有订阅者集合
3、通过反射执行订阅者中的订阅方法
是不是很简单。我们来看一下整个事件分发的流程图
我们简单看一下取消注册的源码EventBus.getDefault().unregister(this);
/** Unregisters the given subscriber from all event classes. */
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 {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
再来看一下:unsubscribeByEventType(subscriber, eventType);
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//获取事件类型的所有订阅者
List<Subscription> 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--;
}
}
}
}
总结一下:
1、首先获取订阅者的所有订阅事件
2、遍历订阅事件
3、根据订阅事件获取所有的订阅了该事件的订阅者集合
4、将该订阅者移除
5、将步骤1中的集合中的订阅者移除
这样就完成了取消订阅的全过程。
Demo下载地址