EventBus用法全解析
来源:互联网 发布:卖esse爱喜烟的淘宝店 编辑:程序博客网 时间:2024/06/06 01:08
以下都是本人收集和总结的内容:
1. 什么是Eventbus
EventBus是一个基于观察者模式的事件发布/订阅框架,开发者通过极少的代码去实现多个模块之间的通信,而不需要以层层传递接口的形式去单独构建通信桥梁。从而降低因多重回调导致的模块间强耦合,同时避免产生大量内部类。拥有使用方便,性能高,接入成本低,支持多线程的优点。
2. 如何实现Eventbus
2.1 定义事件
事件是POJO(plain old java object)类型,不需要什么特别的需求
public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; }
2.2准备订阅者
订阅者实现事件处理方法(也叫做订阅者方法),这个方法会在事件提交的时候被调用。这些是使用@Subscribe注解定义的。请注意EventBus 3的方法名字可以自由选择(不像EventBus 2中约束的那样)。
// 当一个Message Event提交的时候这个方法会被调用@Subscribepublic void onMessageEvent(MessageEvent event){ Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();}// 当一个SomeOtherEvent被提交的时候这个方法被调用。@Subscribepublic void handleSomethingElse(SomeOtherEvent event){ doSomethingWith(event);}
订阅者也需要在bus中注册和注销。只有在订阅者注册的时候,他们才会收到事件。在Android中,Activities和Fragments通常绑定他们的生命周期.
@Overridepublic void onStart() { super.onStart(); EventBus.getDefault().register(this);}@Overridepublic void onStop() { EventBus.getDefault().unregister(this); super.onStop();}
2.3提交事件
在代码中任意位置提交事件。所有当前注册的匹配事件类型的订阅者都会收到事件。
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
2.4线程间传递(线程模式)
在EventBus的事件处理函数中需要指定线程模型,即指定事件处理函数运行所在的想线程。在上面我们已经接触到了EventBus的四种线程模型。那他们有什么区别呢?
在EventBus中的观察者通常有四种线程模型,分别是PostThread(默认)、MainThread、BackgroundThread与Async。
- PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
- MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
- BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
- Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。
@Subscribe(threadMode = ThreadMode.PostThread)public void onMessageEventPostThread(MessageEvent messageEvent) { Log.e("PostThread", Thread.currentThread().getName());}@Subscribe(threadMode = ThreadMode.MainThread)public void onMessageEventMainThread(MessageEvent messageEvent) { Log.e("MainThread", Thread.currentThread().getName());}@Subscribe(threadMode = ThreadMode.BackgroundThread)public void onMessageEventBackgroundThread(MessageEvent messageEvent) { Log.e("BackgroundThread", Thread.currentThread().getName());}@Subscribe(threadMode = ThreadMode.Async)public void onMessageEventAsync(MessageEvent messageEvent) { Log.e("Async", Thread.currentThread().getName());}
分别使用上面四个方法订阅同一事件,打印他们运行所在的线程。首先我们在UI线程中发布一条MessageEvent的消息,看下日志打印结果是什么。
findViewById(R.id.send).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("postEvent", Thread.currentThread().getName()); EventBus.getDefault().post(new MessageEvent()); } });
打印结果如下:
log --> E/postEvent﹕ mainlog --> E/PostThread﹕ mainlog --> E/Async﹕ pool-1-thread-1log --> E/MainThread﹕ mainlog --> E/BackgroundThread﹕ pool-1-thread-2
从日志打印结果可以看出,如果在UI线程中发布事件,则线程模型为PostThread的事件处理函数也执行在UI线程,与发布事件的线程一致。线程模型为Async的事件处理函数执行在名字叫做pool-1-thread-1的新的线程中。而MainThread的事件处理函数执行在UI线程,BackgroundThread的时间处理函数执行在名字叫做pool-1-thread-2的新的线程中。
我们再看看在子线程中发布一条MessageEvent的消息时,会有什么样的结果。
findViewById(R.id.send).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { Log.e("postEvent", Thread.currentThread().getName()); EventBus.getDefault().post(new MessageEvent()); } }).start(); } });
打印结果如下:
log --> E/postEvent﹕ Thread-125log --> E/PostThread﹕ Thread-125log --> E/BackgroundThread﹕ Thread-125log --> E/Async﹕ pool-1-thread-1log --> E/MainThread﹕ main
从日志打印结果可以看出,如果在子线程中发布事件,则线程模型为PostThread的事件处理函数也执行在子线程,与发布事件的线程一致(都是Thread-125)。BackgroundThread事件模型也与发布事件在同一线程执行。Async则在一个名叫pool-1-thread-1的新线程中执行。MainThread还是在UI线程中执行。
上面一个例子充分验证了指定不同线程模型的事件处理方法执行所在的线程。
2.5黏性事件
除了上面讲的普通事件外,EventBus还支持发送黏性事件。何为黏性事件呢?简单讲,就是在发送事件之后再订阅该事件也能收到该事件,跟黏性广播类似。具体用法如下:
订阅黏性事件:
EventBus.getDefault().register(StickyModeActivity.this);
黏性事件处理函数:
@Subscribe(sticky = true)public void XXX(MessageEvent messageEvent) { ......}
发送黏性事件:
EventBus.getDefault().postSticky(new MessageEvent("test"));
处理消息事件以及取消订阅和上面方式相同。
看个简单的黏性事件的例子,为了简单起见我这里就在一个Activity里演示了。
Activity代码:
public class StickyModeActivity extends AppCompatActivity { int index = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sticky_mode); findViewById(R.id.post).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EventBus.getDefault().postSticky(new MessageEvent("test" + index++)); } }); findViewById(R.id.regist).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EventBus.getDefault().registerSticky(StickyModeActivity.this); } }); findViewById(R.id.unregist).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EventBus.getDefault().unregister(StickyModeActivity.this); } }); } @Subscribe(threadMode = ThreadMode.PostThread, sticky = true) public void onMessageEventPostThread(MessageEvent messageEvent) { Log.e("PostThread", messageEvent.getMessage()); } @Subscribe(threadMode = ThreadMode.MainThread, sticky = true) public void onMessageEventMainThread(MessageEvent messageEvent) { Log.e("MainThread", messageEvent.getMessage()); } @Subscribe(threadMode = ThreadMode.BackgroundThread, sticky = true) public void onMessageEventBackgroundThread(MessageEvent messageEvent) { Log.e("BackgroundThread", messageEvent.getMessage()); } @Subscribe(threadMode = ThreadMode.Async, sticky = true) public void onMessageEventAsync(MessageEvent messageEvent) { Log.e("Async", messageEvent.getMessage()); }}
布局代码activity_sticky_mode.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.lling.eventbusdemo.StickyModeActivity"> <Button android:id="@+id/post" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Post"/> <Button android:id="@+id/regist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Regist"/> <Button android:id="@+id/unregist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UnRegist"/></LinearLayout>
代码很简单,界面上三个按钮,一个用来发送黏性事件,一个用来订阅事件,还有一个用来取消订阅的。首先在未订阅的情况下点击发送按钮发送一个黏性事件,然后点击订阅,会看到日志打印结果如下:
log --> E/PostThread﹕ test0log --> E/Async﹕ test0log --> E/MainThread﹕ test0log --> E/BackgroundThread﹕ test0
这就是粘性事件,能够收到订阅之前发送的消息。但是它只能收到最新的一次消息,比如说在未订阅之前已经发送了多条黏性消息了,然后再订阅只能收到最近的一条消息。这个我们可以验证一下,我们连续点击5次POST按钮发送5条黏性事件,然后再点击REGIST按钮订阅,打印结果如下:
log --> E/PostThread﹕ test4log --> E/MainThread﹕ test4log --> E/Async﹕ test4log --> E/BackgroundThread﹕ test4
由打印结果可以看出,确实是只收到最近的一条黏性事件。
2.6配置
EventBusBuilder用来配置EventBus。比如,如果一个提交的事件没有订阅者,可以使EventBus保持安静。
EventBus eventBus = EventBus.builder().logNoSubscriberMessages(false) .sendNoSubscriberEvent(false).build()
另一个例子是当一个订阅者抛出一个异常的失败。注意:默认情况下,EventBus捕获异常从onEvent方法中抛出并且发出一个SubscriberExceptionEvent ,这个事件可以不必处理。
EventBus eventBus = EventBus.builder().throwSubscriberException(true).build();
配置默认EventBus实例使用EventBus.getDefault()是一种简单的方法来获取共享的EventBus实例。EventBusBuilder也可以使用installDefaultEventBus()方法来配置这个默认的实例。比如,当在onEvent方法中发生异常的时候,可以配置默认的EventBus实例来重新抛出异常。建议在使用DEBUG模式的时候这么使用,因为这样app会因为这个异常而崩溃。
EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).installDefaultEventBus();
注意:只有在默认EventBus实例在第一次使用之前这么配置一次。后续调用installDefaultEventBus() 会抛出异常。这确保应用程序的行为一致。可以在Application类中配置默认的EventBus。
2.7订阅者索引
对于上面所描述的EventBus的功能,是通过Java反射来获取订阅方法,这样以来大大降低了EventBus的效率,同时也影响了我们应用程序的效率。其实对于反射的处理解析不仅仅只能够通过Java反射的方式来进行,还能够通过apt(Annotation Processing Tool)来处理。为了提高效率,EventBus提供这中方式来完成EventBus的执行过程。下面就来看一下对于EventBus的另一种使用方式。
在Project的build.gradle中添加如下代码:
buildscript { dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' }}
然后在app的build.gradle中添加如下代码。
apply plugin: 'com.neenbedankt.android-apt'dependencies { compile 'org.greenrobot:eventbus:3.0.0' apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'}apt { arguments { eventBusIndex "com.example.myapp.MyEventBusIndex" }}
重新rebuild之后会在build目录下面生成MyEventBusIndex文件,文件名可以自定义。下面就来看一下如何使用这个MyEventBusIndex.我们可以自定义设置自己的EventBus来为其添加MyEventBusIndex对象。代码如下所示:
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
我们也能够将MyEventBusIndex对象安装在默认的EventBus对象当中。代码如下所示:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();// Now the default instance uses the given index. Use it like this:EventBus eventBus = EventBus.getDefault();
剩下操作与之前EventBus的一样。当然也建议通过添加订阅者索引这种方式来使用EventBus,这样会比通过反射的方式来解析注解效率更高。
3.结合Eventbus源码深度学习
3.1类关系图
3.2源码分析
3.2.1创建EventBus
一般情况下我们都是通过EventBus.getDefault()获取到EventBus对象,从而在进行register()或者post()等等,所以我们看看getDefault()方法的实现:
public class EventBus {static volatile EventBus defaultInstance; .....省略代码..... public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; } .....省略代码.....}
这里就是设计模式里我们常用的单例模式了,目的是为了保getDefault()
得到的都是同一个实例。如果不存在实例,就调用了EventBus的构造方法:
public class EventBus {private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;private final Map<Object, List<Class<?>>> typesBySubscriber;private final Map<Class<?>, Object> stickyEvents;private final HandlerPoster mainThreadPoster;private final BackgroundPoster backgroundPoster;private final AsyncPoster asyncPoster;private final int indexCount;private final SubscriberMethodFinder subscriberMethodFinder;private final boolean logSubscriberExceptions;private final boolean logNoSubscriberMessages;private final boolean sendSubscriberExceptionEvent;private final boolean sendNoSubscriberEvent;private final boolean throwSubscriberException;private final boolean eventInheritance;private final ExecutorService executorService;.....省略代码.....public EventBus() { this(DEFAULT_BUILDER);}EventBus(EventBusBuilder builder) { //key:订阅的事件,value:订阅这个事件的所有订阅者集合 subscriptionsByEventType = new HashMap<>(); //key:订阅者对象,value:这个订阅者订阅的事件集合 typesBySubscriber = new HashMap<>(); //粘性事件 key:粘性事件的class对象, value:事件对象 stickyEvents = new ConcurrentHashMap<>(); //事件主线程处理 mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); //事件 Background 处理 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;}.....省略代码.....}
可以看出是通过初始化了一个EventBusBuilder()对象来分别初始化EventBus的一些配置,当我们在写一个需要自定义配置的框架的时候,这种实现方法非常普遍,将配置解耦出去,使我们的代码结构更清晰.并且在这里注意到对于EventBus可以采用单实例模式获取,但是EventBus的构造方法为公共的。很显然也就是说明了在应用中可以存在多个EventBus,对于存在多个的EventBus情况下,它们之间相互独立,会发布和订阅各自的事件进行接收执行。
3.2.2注册过程源码分析
3.2.2.1register()方法的实现
public class EventBus { .....省略代码..... private final SubscriberMethodFinder subscriberMethodFinder; public void register(Object subscriber) { //订阅者的class对象 Class<?> subscriberClass = subscriber.getClass(); //通过subscriberMethodFinder来找到订阅者订阅了有哪些事件.返回一个SubscriberMethod对象的List //SubscriberMethod里包含了这个方法的Method对象, //及响应订阅是在哪个线程的ThreadMode, //及订阅的事件类型 eventType, //及订阅的优 先级priority,及接收粘性sticky事件的boolean值. List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } } .....省略代码.....}
在这里可以看到register这个方法中的代码很简短。对于register中的参数,就是我们的订阅者,也就是我们经常传入的this对象。在这个方法中主要完成了两件事情。首先通过findSubscriberMethods方法来查找订阅者中所有的订阅方法。然后遍历订阅者的订阅方法来完成订阅者的订阅操作。下面来详细的分析这两步的实现过程。
3.2.2.2SubscriberMethodFinder的实现
简单描述SubscriberMethodFinder类就是用来查找和缓存订阅者响应函数的信息的类。而SubscriberMethod类主要就是用保存订阅方法的Method对象,线程模式,事件类型,优先级,是否粘性事件等属性。所以我们首先要知道怎么能获得订阅者响应函数的相关信息。
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()的所有需要的信息,如果再配置EventBus的时候通过EventBusBuilder配置:eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();来将编译生成的MyEventBusIndex配置进去,这样就能在SubscriberMethodFinder类中直接查找出订阅类的信息,就不需要再利用注解判断了,当然这种方法是作为EventBus的可选配置,SubscriberMethodFinder同样提供了通过注解来获得订阅类信息的方法,下面我们就来看findSubscriberMethods()到底是如何实现的:
public class SubscriberMethodFinder{ .....省略代码..... private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>(); private final boolean ignoreGeneratedIndex; List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //从缓存中获取SubscriberMethod集合 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } //是否忽略注解器生成的MyEventBusIndex类 if (ignoreGeneratedIndex) { //通过反射获取subscriberMethods subscriberMethods = findUsingReflection(subscriberClass); } else { //通过注解器生成的MyEventBusIndex信息获取subscriberMethods, //如果没有配置MyEventBusIndex,依然通过通过反射获取subscriberMethods 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缓存 METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; } } .....省略代码.....}
总结:在这个类的 逻辑依然也是十分的清晰。首先会从缓存中查找是否有订阅方法的集合,若是存在则直接返回缓存中的该订阅者的订阅方发集合。若是不存在,便从订阅者中找出全部的订阅方法。对于ignoreGeneratedIndex属性表示是否忽略注解器生成的MyEventBusIndex(在项目重新rebuild以后,会自动生成在build文件夹下,类名也可以自己定义)。如何生成MyEventBusIndex类以及他的使用,可以参考官方文档。ignoreGeneratedIndex的默认值为false,可以通过EventBusBuilder来设置它的值。在这里会根具ignoreGeneratedIndex的值来采用不同的方式获取订阅方法的集合subscriberMethods。在获得subscriberMethods以后,如果订阅者中不存在@Subscribe注解且为public的订阅方法,则会抛出异常。这也就是说对于订阅者若是要成功注册到EventBus中,在订阅者中必须存在通过@Subscribe注解且为public类型的订阅方法。在成功获取到订阅方法集合以后,便将订阅方法集合添加到缓存中。对于这个缓存它是以订阅者的类作为key,订阅方法集合作为value的Map类型。
下面我们继续来看findUsingInfo()方法:
class SubscriberMethodFinder {private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { //创建和初始化FindState对象 FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { //获取订阅者信息,没有配置MyEventBusIndex返回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(); } //回收处理findState,并返回订阅方法的List集合 return getMethodsAndRelease(findState);}}
在FindState里面,它保存了一些订阅者的方法以及对订阅方法的校验。通过initForSubscriber初始化了FindState的clazz属性。于是下面便进入while循环当中。通过getSubscriberInfo方法来获取订阅者信息。在我们开始查找订阅方法的时候并没有忽略注解器为我们生成的索引MyEventBusIndex,如果我们通过EventBusBuilder配置了MyEventBusIndex,便会获取到subscriberInfo。而在MyEventBusIndex中会将订阅方法相关的信息存放在SubscriberMethodInfo类中,这个时候就不在需要通过注解进行获取订阅方法。如果没有配置MyEventBusIndex,便会执行findUsingReflectionInSingleClass方法,将订阅方法保存到findState中。最后再通过getMethodsAndRelease方法对findState做回收处理并反回订阅方法的List集合。
下面我们就来看findUsingReflection()方法是如何实现的:
public class SubscriberMethodFinder{ private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { //FindState 用来做订阅方法的校验和保存 FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { //通过反射来获得订阅方法信息 findUsingReflectionInSingleClass(findState); //查找父类的订阅方法 findState.moveToSuperclass(); } //获取findState中的SubscriberMethod(也就是订阅方法List)并返回 return getMethodsAndRelease(findState); }}
这里通过FindState类来做订阅方法的校验和保存,并通FIND_STATE_POOL静态数组来保存FindState对象,可以使FindState复用,避免重复创建过多的对象.最终是通过findUsingReflectionInSingleClass()来具体获得相关订阅方法的信息的:
public class SubscriberMethodFinder{ 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; } //遍历Method for (Method method : methods) { int modifiers = method.getModifiers(); // 事件处理方法必须为public,这里过滤掉所有非public方法 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(); //实例化SubscriberMethod对象并添加 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { // 如果某个方法加了@Subscribe注解,并且不是1个参数, 则抛出EventBusException异常 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)) { // 如果某个方法加了@Subscribe注解, 并且不是public修饰,则抛出EventBusException异常 String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } } }}
在这里主要是使用了Java的反射和对注解的解析。首先通过反射来获取订阅者中所有的方法。并根据方法的类型,参数和注解来找到订阅方法。找到订阅方法后将订阅方法相关信息保存到FindState当中。我们订阅类的所有SubscriberMethod都已经被保存了,最后再通过getMethodsAndRelease()返回List<SubscriberMethod>至此,所有关于如何获得订阅类的订阅方法信息即:SubscriberMethod对象就已经完全分析完了,下面我们来看subscribe()是如何实现的.
3.2.2.3subscribe()方法的实现
好的,这里我们回到3.2.2.1的subscribe(subscriber, subscriberMethod);中去,通过这个方法,我们就完成了注册,下面看一下subscribe()的实现:
public class EventBus { .....省略代码..... private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; private final Map<Object, List<Class<?>>> typesBySubscriber; private final boolean eventInheritance; private final Map<Class<?>, Object> stickyEvents; //必须在同步代码块里调用 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { //获取订阅方法中的订阅事件 Class<?> eventType = subscriberMethod.eventType; //创建一个Subscription来保存订阅者和订阅方法 Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //获取当前订阅事件中Subscription的List集合 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { //该事件对应的Subscription的List集合不存在,则重新创建并保存在subscriptionsByEventType中 subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { //判断是订阅者是否已经被注册 if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } //将newSubscription按照订阅方法的优先级插入到subscriptions中 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); } //将当前的订阅事件添加到subscribedEvents中 subscribedEvents.add(eventType); // 如果该事件处理方法为粘性事件,即设置了“sticky = true” ,则需要调 用checkPostStickyEventToSubscription // 判断是否有粘性事件需要处理,如果需要处理则触发一次事件处理函数 if (subscriberMethod.sticky) { //eventInheritance 表示是否分发订阅了响应事件类父类事件的方法 if (eventInheritance) { 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); } } } .....省略代码.....}
以上代码便是订阅者真正的注册过程。首先会根据subscriber和subscriberMethod来创建一个Subscription对象。之后根据事件类型获取或创建一个Subscription集合subscriptions并添加到typesBySubscriber对象中。最后将刚才创建的Subscription对象添加到subscriptions之中。于是这样就完成了一次订阅者的注册操作。
现在再来看这张图就会特别清晰EventBus的register()过程了:
3.2.3事件分发过程源码分析
通过EventBus.getDefault().post("str");来发送一个事件,所以我们就从这行代码开始分析,首先看看post()方法是如何实现的:
class EventBus{ private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); } }; public void post(Object event) { //PostingThreadState保存着事件队列和线程状态信息 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 { // 循环处理eventQueue中的每一个event对象 while (!eventQueue.isEmpty()) { //发送单个事件 postSingleEvent(eventQueue.remove(0), postingState); } } finally { // 处理完之后重置postingState的一些标识信息 postingState.isPosting = false; postingState.isMainThread = false; } } } final static class PostingThreadState { //通过post方法参数传入的事件集合 final List<Object> eventQueue = new ArrayList<Object>(); boolean isPosting; //是否正在执行postSingleEvent()方法 boolean isMainThread; Subscription subscription; Object event; boolean canceled; }}
ThreadLocal理解:ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。其内部原理是通过生成一个它包裹的泛型对象的数组,在不同的线程会有不同的数组索引值,通过这样就可以做到每个线程通过 get() 方法获取的时候,取到的只能是自己线程所对应的数据。
在 EventBus 中, ThreadLocal 所包裹的是一个PostingThreadState类,它仅仅是封装了一些事件发送中过程所需的数据。PostingThreadState中保存了事件队列,以及线程的一些状态信息。首先从PostingThreadState对象中取出事件队列,然后再将当前的事件插入到事件队列当中。最后将队列中的事件依次交由postSingleEvent方法进行处理,并移除该事件。
接下来我们来看postSingleEvent()
public class EventBus { private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; //获取所有事件并存放在List中,这里表示事件存在继承关系,向上查找事件的父类 if (eventInheritance) { //查找eventClass类所有的父类以及接口 List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); //循环postSingleEventForEventType for (int h = 0; h < countTypes; h++) { Class<?> clazz = eventTypes.get(h); //只要右边有一个为true,subscriptionFound就为true subscriptionFound |= postSingleEventForEventType(event,postingState, clazz); } } else { //post单个 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) { //发送一个NoSubscriberEvent事件,如果我们需要处理这种状态,接收这个事件就可以了 post(new NoSubscriberEvent(this, event)); } } }}
对于eventInheritance表示是否向上查找事件的父类。它的默认值为true,可以通过在EventBusBuilder中来进行配置。当eventInheritance为true时,则通过lookupAllEventTypes找到所有的父类事件并存发在List中,然后通过postSingleEventForEventType方法对事件逐一处理。下面就跟进到postSingleEventForEventType方法中查看一下。
public class EventBus { private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { //同步取出该事件对应的Subscription集合 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: // 如果该事件处理函数没有指定线程模型或者线程模型为 PostThread // 则调用invokeSubscriber在post的线程中执行事件处理函数 invokeSubscriber(subscription, event); break; case MAIN: // 如果该事件处理函数指定的线程模型为MainThread // 并且当前post的线程为主线程,则调用invokeSubscriber在当前线程(主线程)中执行事件处理函数 // 如果post的线程不是主线程,将使用mainThreadPoster.enqueue该事件处理函数添加到主线程的消息队列中 if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; case BACKGROUND: // 如果该事件处理函数指定的线程模型为BackgroundThread // 并且当前post的线程为主线程,则调用backgroundPoster.enqueue // 如果post的线程不是主线程,则调用invokeSubscriber在当前线程(非主线程)中执行事件处理函数 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); } }}
还记得在订阅者进行注册时候,以订阅事件作为Key,将Subscription的List集合作为Value保存到了一个Map集合当中。而就在这个方法中通过事件类型取出Subscription的List集合,然后调用了postToSubscription方法来处理事件并执行订阅方法。这一路对订阅事件的分发,总算差不多要到头了。在这里取出订阅方法的线程模式,之后根据订阅方法所设置的线程模式来选择线程来执行订阅方法的线程。 订阅方法的线程模式为MAIN的时候。提交事件的线程是主线程则直接执行invokeSubscriber方法。否则加入到mainThreadPoster对象的队列中,而mainThreadPoster对象他是HandlerPoster对象。HandlerPoster继承自Handler,也即是通过Handler将订阅方法切换到主线程执行。当执行订阅方法需要在子线程中的时候。EventBus是通过Java中的newCachedThreadPool线程池来创建线程并执行的任务。而BackgroundPoster和AsyncPoster它们都是一个Runable对象。而执行它们们run方法也是在enqueue方法中。
那么mainThreadPoster、backgroundPoster和asyncPoster分别是HandlerPoster、BackgroundPoster和AsyncPoster的对象,其中HandlerPoster继承自Handle,BackgroundPoster和AsyncPoster继承自Runnable。我们主要看看HandlerPoster。
public class EventBus{private final HandlerPoster mainThreadPoster; EventBus(EventBusBuilder builder) { //事件主线程处理 mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); }}
在EventBus的构造函数中,我们看到mainThreadPoster初始化的时候,传入的是Looper.getMainLooper()。所以此Handle是运行在主线程中的。mainThreadPoster.enqueue方法:
final class HandlerPoster extends Handler { //单例池,复用对象 private final PendingPostQueue queue; private final int maxMillisInsideHandleMessage; private final EventBus eventBus; private boolean handlerActive; HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { super(looper); this.eventBus = eventBus; this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; queue = new PendingPostQueue(); } void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); synchronized (this) { queue.enqueue(pendingPost); if (!handlerActive) { handlerActive = true; //enqueue方法最终会调用sendMessage方法,所以该Handle的handleMessage方法会被调用。 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; } }
在该方法中,最终还是会调用eventBus.invokeSubscriber调用事件处理函数。
BackgroundPoster和AsyncPoster继承自Runnable,并且会在enqueue方法中调用eventBus.getExecutorService().execute(this);具体run方法大家可以自己去看源码,最终都会调用eventBus.invokeSubscriber方法。我们看看eventBus.invokeSubscriber方法的源码:
public class Eventbus{ void invokeSubscriber(PendingPost pendingPost) { Object event = pendingPost.event; Subscription subscription = pendingPost.subscription; PendingPost.releasePendingPost(pendingPost); if (subscription.active) { invokeSubscriber(subscription, 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); }}}
该方法最终会通过反射来调用事件处理函数。至此,整个post过程分析完了。
总结一下整个post过程,大致分为3步:
- 将事件对象添加到事件队列eventQueue中等待处理
- 遍历eventQueue队列中的事件对象并调用postSingleEvent处理每个事件
- 找出订阅过该事件的所有事件处理函数,并在相应的线程中执行该事件处理函数
3.2.4解除注册源码分析
看完了上面的分析,解除注册就相对容易了,解除注册只要调用unregister()方法即可,实现如下:
public class EventBus { private final Map<Object, List<Class<?>>> typesBySubscriber; public synchronized void unregister(Object subscriber) { //通过typesBySubscriber来取出这个subscriber订阅者订阅的事件类型, List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { //分别解除每个订阅了的事件类型 for (Class<?> eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } //从typesBySubscriber移除subscriber typesBySubscriber.remove(subscriber); } else { Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }}
然后接着看unsubscribeByEventType()方法的实现:
public class EventBus{private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { //subscriptionsByEventType里拿出这个事件类型的订阅者列表. 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--; } } } }}
最终分别从typesBySubscriber和subscriptions里分别移除订阅者以及相关信息即可.
转载:WuXiao_
原文链接:http://www.jianshu.com/p/5dd494e6d506
- EventBus用法全解析
- eventbus全解析
- EventBus全解析
- Android EventBus 用法解析
- OkHttp3用法全解析
- Volley用法全解析
- EventBus 解析
- EventBus解析
- EventBus3.0用法全解析
- EventBus3.0用法全解析
- AndroidEventBus3.0用法全解析
- Android网络编程OkHttp3用法全解析
- 001_a标签用法全解析
- Android网络编程-OkHttp3用法全解析
- EventBus 源码解析
- EventBus 源码解析
- EventBus 源码解析
- EventBus 源码解析
- 对计算机科学中的算法学习总结(32个算法)
- python零碎知识(8)--文件和流
- 数字证书中主题(Subject)中字段的含义
- 简单入门小程序 01
- CXF的异常处理
- EventBus用法全解析
- 电商笔记-07(solr全文检索lunix中的搭建与商品的上架)
- SonarQube 之 gitlab-plugin 配合 gitlab-ci 完成每次 commit 代码检测
- 2017年,CRM经历了哪些发展
- 【选择题】Java基础测试五(15道)
- 浅谈关于数据结构中的树
- Jenkins安装+配置Git +Maven的自动化构建(上)
- Hello Redis
- CF 438D The Child and Sequence [线段树]