EventBus简介EventBus是一种用于Android的发布/订阅事件总线。我们经常用来在不同界面,不同线程传递数据,它解耦了事件发送方和事件处理方。 虽然Android本身提供了LocalBroadcastReceiver类可以实现类似的功能,但是LocalBroadcastReceiver使用起来稍微繁琐,而且传递数据大小也受intent限制。EventBus角色EventBus主要有一下三个角色:Event事件,可以是任意引用类型,不能是基础数据类型如int,char。他是EventBus传递,处理的对象。Subscriber事件订阅者,订阅者必须有Subscribe注解的订阅方法,用来处理事件。Publisher事件发布者,可以在任意线程发布事件。事件订阅者收到后会进行处理。基本使用添加依赖implementation org.greenrobot:eventbus:3.3.1定义事件public class MessageEvent {}这一步不是必须的,因为事件可以是任意引用类型,所以可以使用一些已经存在的类作为事件类,比如String注册/反注册// 注册 protected void onCreate(Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventBus.getDefault().register(this); } // 反注册 protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); }注册完了之后必须反注册,否则造成内存泄漏,在没有反注册之前,不能重复注册,不然会抛出异常。事件处理Subscribe(threadMode ThreadMode.MAIN,priority1,stickytrue) public void handleMessage(MessageEvent event) { Log.i(EventBusTest, event.toString()); }在上一步注册的类里面添加一个方法,用于事件处理,方法名任意,但是只能有一个参数,而且修饰符必须是public的,不能使用static,abstract修饰,然后使用Subscribe注解。Subscribe提供了一些可选参数让我们在不同场景选用。事件发送EventBus.getDefault().post(new MessageEvent());在需要发送事件的地方调用Eventbus的post()方法发送事件。Subscribe参数详解Subscribe有3个参数,threadMode用于指定线程模型,sticky指定是否处理粘性事件,priority指定优先级。threadModeEventBus允许事件发送的线程和事件处理的线程不一样,我们可以通过threadMode指定,ThreadMode是一个枚举类型,有以下五种取值:POSTING订阅者的订阅方法将在发布事件的同一线程中被调用,这种模式下不会涉及线程切换,所以开销比较小,但是可能发布事件的线程是主线程,所以需要避免在订阅方法中处理耗时操作MAIN订阅方法将在UI线程被调用,如果发布事件的线程也是UI线程,则可以直接执行调用订阅方法,否则会使用Handler切换到UI线程MAIN_ORDERED与MAIN类似,只不过不管发布线程在不在UI线程,都会由Handler处理,这确保了post()的调用是非阻塞的BACKGROUND订阅方法将在非UI线程被调用,如果发布事件的线程在UI线程,则会创建一个线程来执行订阅方法,否则直接执行,为避免后续事件的发送或调用,需要避免在订阅方法中处理耗时操作ASYNC订阅方法的调用线程将独立于发送事件的线程,所以这种情况下可以做耗时操作,但是需要避免在同一时间进行大量的异步订阅,控制并发线程的数量。ThreadMode.POSTING事件发送new Thread(() - EventBus.getDefault().post(new MessageEvent(Thread.currentThread().getName()))).start();在发布线程处理事件Subscribe(threadMode ThreadMode.POSTING) public void postingButton(MessageEvent event) { Log.i(EventBusTest, 事件发送线程: event.name ,事件处理线程: Thread.currentThread().getName()); }结果EventBusTest: 事件发送线程:Thread-7,事件处理线程:Thread-7可以看出订阅者的订阅方法将在发布事件的同一线程中被调用。ThreadMode.MAIN事件发送long start System.currentTimeMillis(); EventBus.getDefault().post(new MessageEvent(Thread.currentThread().getName())); long end System.currentTimeMillis(); Log.i(EventBusTest, 间隔 (end - start) ms 才后发送下一个事件); new Thread(() - EventBus.getDefault().post(new MessageEvent(Thread.currentThread().getName()))).start();处理事件Subscribe(threadMode ThreadMode.MAIN) public void mainButton(MessageEvent event) { Log.i(EventBusTest, 事件发送线程: event.name ,事件处理线程: Thread.currentThread().getName()); SystemClock.sleep(1000); }结果输出EventBusTest: 事件发送线程: main,事件处理线程: main EventBusTest: 间隔 1001ms 才后发送下一个事件 EventBusTest: 事件发送线程: Thread-7,事件处理线程: main可以看出,即使发送线程不在主线程,最后处理的时候也会切换到主线程,而且因为处理事件的方法做了耗时操作,所以隔了1s多才发送第二个事件。ThreadMode.MAIN_ORDERED事件发送与上面一样,处理事件的方法调整下,将threadMode设置为ThreadMode.MAIN_ORDEREDSubscribe(threadMode ThreadMode.MAIN_ORDERED) public void mainOrderButton(MessageEvent event) { Log.i(EventBusTest, 事件发送线程: event.name ,事件处理线程: Thread.currentThread().getName()); SystemClock.sleep(1000); }结果输出EventBusTest: 间隔 0ms 才后发送下一个事件 EventBusTest: 事件发送线程: main,事件处理线程: main EventBusTest: 事件发送线程: Thread-8,事件处理线程: main可以看出MAIN_ORDERED与MAIN相似的是都是切换到MAIN线程处理事件,不一样的是ThreadMode.MAIN这种模型下,前一个事件处理可能会阻塞下一个事件的发送,而ThreadMode.MAIN_ORDERED不会这里说的是“可能阻塞”,至于会不会阻塞取决于事件的发送线程,如果一个事件在非主线程发送,结果其实与MAIN_ORDERED一样,不会阻塞,如果是在主线程发送,线程模型又是指定的ThreadMode.MAIN,则会先立即执行处理此事件的方法,等到方法执行完毕,才会发送下一个事件。ThreadMode.BACKGROUND事件发送new Thread(() - { long start System.currentTimeMillis(); EventBus.getDefault().post(new MessageEvent(Thread.currentThread().getName(), start)); long end System.currentTimeMillis(); Log.i(EventBusTest, 子线程发送耗时 (end - start) ms); }).start(); //保证上下两种情况互不干扰 SystemClock.sleep(2000); long start System.currentTimeMillis(); EventBus.getDefault().post(new MessageEvent(Thread.currentThread().getName(), System.currentTimeMillis())); long end System.currentTimeMillis(); Log.i(EventBusTest, 主线程发送耗时 (end - start) ms);这里在非MAIN线程和MAIN线程都分别发送了事件,主要是为了体现发布线程对结果的影响。事件处理Subscribe(threadMode ThreadMode.BACKGROUND,priority 1) public void bgButton(MessageEvent event) { long time System.currentTimeMillis() - event.sendTime; Log.i(EventBusTest, 优先级1:事件发送线程: event.name ,事件处理线程: Thread.currentThread().getName() 发送 time m后才开始处理); SystemClock.sleep(500); } Subscribe(threadMode ThreadMode.BACKGROUND,priority 2) public void bgButton1(MessageEvent event) { long time System.currentTimeMillis() - event.sendTime; Log.i(EventBusTest, 优先级2:事件发送线程: event.name ,事件处理线程: Thread.currentThread().getName() 发送 time m后才开始处理); SystemClock.sleep(500); }结果输出EventBusTest: 优先级2:事件发送线程: Thread-7,事件处理线程: Thread-7 发送1 m后才开始处理 EventBusTest: 优先级1:事件发送线程: Thread-7,事件处理线程: Thread-7 发送501 m后才开始处理 EventBusTest: 子线程发送耗时 1002ms EventBusTest: 主线程发送耗时 4ms EventBusTest: 优先级2:事件发送线程: main,事件处理线程: pool-3-thread-1 发送4 m后才开始处理 EventBusTest: 优先级1:事件发送线程: main,事件处理线程: pool-3-thread-1 发送506 m后才开始处理从日志可以看出ThreadMode.BACKGROUND线程模型下如果发布线程不是MAIN线程,则事件处理是在当前线程进行,而且同一个线程内是串行执行的,上一个执行完了才会执行下一个。如果发布线程是MAIN线程,则事件处理线程会切换到非MAIN线程,而且事件处理不会阻塞MAIN线程,同一个线程内也是串行执行。ThreadMode.ASYNC事件发送与上面一致,事件处理方法体也一样,只是修改下threadModeSubscribe(threadMode ThreadMode.ASYNC)结果输出EventBusTest: 子线程发送耗时 1ms 2EventBusTest: 事件发送线程: Thread-7,事件处理线程: pool-3-thread-1 发送0 m后才开始处理 EventBusTest: 事件发送线程: Thread-7,事件处理线程: pool-3-thread-2 发送0 m后才开始处理 EventBusTest: 主线程发送耗时 2ms EventBusTest: 事件发送线程: main,事件处理线程: pool-3-thread-1 发送1 m后才开始处理 EventBusTest: 事件发送线程: main,事件处理线程: pool-3-thread-2 发送0 m后才开始处理与ThreadMode.BACKGROUND相似的是,事件处理线程都不会是MAIN线程,不一样的是即使ThreadMode.ASYNC是在非MAIN线程发送的事件,处理事件的线程也不会是原线程,而且事件处理是异步的,不会阻塞原线程,多个事件处理方法也是并行执行。sticky粘性事件是指发送了事件之后在注册订阅者,订阅者依然能收到事件。需要注意的是粘性事件是以参数类型来缓存的,同一类型的事件只会保留最近的一个。priorityEventBus可以指定事件处理方法的优先级。默认情况下为0。数值越大优先级越高,优先级只有在相同的线程模式下才有意义。首先我们发送一个事件MessageEvent event new MessageEvent(Thread.currentThread().getName()); event.info 项目100天完成; EventBus.getDefault().post(event);然后定义三个不同优先级的事件处理方法Subscribe(priority 0,threadMode ThreadMode.POSTING) public void priorityButton0(MessageEvent event) { Log.i(EventBusTest,码农收到消息: event.info); } Subscribe(priority 1,threadMode ThreadMode.POSTING) public void priorityButton1(MessageEvent event) { Log.i(EventBusTest,经理收到消息: event.info); EventBus.getDefault().cancelEventDelivery(event); } Subscribe(priority 2,threadMode ThreadMode.POSTING) public void priorityButton2(MessageEvent event) { Log.i(EventBusTest,主管收到消息: event.info); event.info 项目10天完成; }既然事件是按照优先级处理的,而且处理的时候能拿到事件本身,那么高优先级的就可以对事件进行修改和取消,所以结果输出如下:EventBusTest: 主管收到消息: 项目100天完成 EventBusTest: 经理收到消息: 项目10天完成优先级为2的先收到原始事件,他处理完成后对事件进行了修改,优先级为1的收到的就是他修改之后的事件,在优先级为1的方法处理完成之后将事件取消,导致优先级为0的就收不到事件了。需要注意的是事件的取消是有限制的它的线程模型必须是ThreadMode.POSTING。取消的事件不能为null并且必须和发送中的事件一样。只有在事件发送中才能取消,否则事件都发送完了,取消了个寂寞。订阅者索引经过上面的使用我们基本掌握了EventBus常见用法,但是这并没有享受到EventBus3.x给我们带来的性能提升。我们知道EventBus3.0之后就引入了编译时注解,避免了反射查找订阅方法带来的性能损耗。虽然上面我们引入的是3.3.1的包,但是查找订阅方法使用的依然是反射。虽然默认情况下EventBus3.x会优先使用编译时注解,但是在上面的使用过程中我们并没有提供订阅者索引,导致最后降级为反射查找。要想享受到性能提升,首先需要配置索引类的信息android { defaultConfig { kapt { arguments { arg(eventBusIndex,com.example.test.MyEventBusIndex) } } } } dependencies { kapt org.greenrobot:eventbus-annotation-processor:3.3.1 }在build之后,EventBus会生成MyEventBusIndex文件。里面包含了我们的订阅方法的信息。然后通过EventBusBuilder配置索引类EventBus eventBus EventBus.builder().addIndex(new MyEventBusIndex()).build()虽然通过EventBusBuilder#buildr()可以获取EventBus实例,但是我们在平时使用更多的是EventBus.getDefault()获取,为了让getDefault()方法获取的实例设置了索引类,我们需要使用installDefaultEventBus()方法初始化默认实例defaultInstance。installDefaultEventBus()只能在defaultInstance为null的时候调用,所以一般在Application#onCreate()中调用。事件继承默认情况下,发送一个事件,该事件类父类的订阅方法也能得到调用,如果想避免这种情况,只需要调用EventBusBuilder#eventInheritance()方法将eventInheritance属性设置为false就可以了。