EventBus详解

区块链

  最近面试被问到消息传递的框架用过哪些,这还难到我,EventBus了,然后紧接着就被问到用的是2.X还是3.0,想了一下,用的是最新的3.0版本,但是殊不知3.0针对2.x来说除了编码上更简洁以外,性能上还有很多提升,下面由我徐徐道来

  1,EventBus中的几个角色

  Event事件,

  Subscriber事件订阅者

  Publisher事件发布者

  ThreadMode定义事件运行的线程

  他们之间工作流程如下图

  官方图

  先说下EventBus的使用(没有序列化和反序列化的性能消耗问题)

  1,初始化,

  EventBus eventBus=EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).build();可以看出是通过建造者模式进行创建的,和Android的dialog创建类似

  示例

  1,在build.gradle中添加依赖

  compile org.greenrobot:eventbus:3.1.12,定义event,这里我建议做项目的时候在model类中添加一个event包,在event包中创建一个BaseEvent,以后所有的event都继承此event

  我这里定义了一个event如下

  /*** author:sparkhuu* email:sparkhuu@gmail.com*/public class PurchaseEvent exts BaseEvent{private String code; private String msg; public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}}3,在需要的地方进行订阅(注意,此订阅会对所有的消息感兴趣)(一般在onCreate()生命周期中进行)

  EventBus.getDefault().register(this);//订阅4,为了避免OOM,在刚刚订阅的地方同时进行解除订阅(一般在onDestroy()中进行)

  EventBus.getDefault().unregister(this);//解除订阅5,发布事件

  EventBus.getDefault().post(new PurchaseEvent());6,在刚订阅的地方对他感兴趣的消息进行处理

  @Subscribe(threadMode = ThreadMode.MAIN) //在ui线程执行public void onPurchaseEvent(PurchaseEvent event) { Log.e(TAG, "event---->" + event.getCount()); }注意:这里用到了注解,一般人会想到注解会影响性能,但其实java注解是分场景的:

  一种是编译期注解,这个是在编译器编译期间会进行解析的,大家都知道.java文件通过javac指令会编译成.class文件,(Android多了一步dex)编译期注解在这里就会被解析,所以不会影响最终apk运行性能。

  另外一种是运行时注解,这个是在RunTime通过反射进行解析加载的,Class.forName(XXX.class),这个是影响性能的。

  好了,回到正题,通过源码可以发现,这里的threadMode分为4个

  /** Copyright (C) 2012-2022 Markus Junginger, greenrobot (http://greenrobot.org)** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.greenrobot.eventbus;/*** Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus.* EventBus takes care of threading indepently from the posting thread.** @see EventBus#register(Object)* @author Markus*/public enum ThreadMode {/** * Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery * implies the least overhead because it avoids thread switching completely. Thus this is the recommed mode for * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread. */POSTING,/** * On Android, subscriber will be called in Androids main thread (UI thread). If the posting thread is * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread. * If not on Android, behaves the same as {@link #POSTING}. */MAIN,/** * On Android, subscriber will be called in Androids main thread (UI thread). Different from {@link #MAIN}, * the event will always be queued for delivery. This ensures that the post call is non-blocking. */MAIN_ORDERED,/** * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread. */BACKGROUND,/** * Subscriber will be called in a separate thread. This is always indepent from the posting thread and the * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number * of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications. */ASYNC}通过注释不难发现他很多线程处理,并非单单ui线程

  同时,对事件进行订阅时,也可以订阅他的优先级,(和广播类似)

  @Subscribe(threadMode = ThreadMode.MAIN,priority = 100) //在ui线程执行 优先级100public void onPurchaseEvent(PurchaseEvent event) { Log.e(TAG, "event---->" + event.getCount()); }同时如果发送有序广播的话,优先级高的可以终止事件往下传递

  EventBus.getDefault().cancelEventDelivery(event) ;//优先级高的订阅者可以终止事件往下传递最后进行混淆

  -keepattributes *Annotation*-keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe <methods>;}-keep enum org.greenrobot.eventbus.ThreadMode { *; }# Only required if you use AsyncExecutor-keepclassmembers class * exts org.greenrobot.eventbus.util.ThrowableFailureEvent { <init>(java.lang.Throwable);}同样EventBus在3.0还了粘贴事件,和粘贴广播类似,可以简单理解订阅在发布事件之后,同样可以收到消息,可以很好地解决post与register同时执行时的异步问题

  @Subscribe(threadMode = ThreadMode.MAIN,sticky = true) //在ui线程执行public void onPurchaseEvent(PurchaseEvent event) { Log.e(TAG, "event---->" + event.getCount()); }订阅事件和解除事件和普通事件处理方式一样

  发送粘贴事件

  EventBus.getDefault().postSticky(new PurchaseEvent());大家都知道粘贴广播属于常驻广播,不用了要进行移除,否则会导致OOM,同理粘性事件也需要在不用的时候进行移除

  EventBus.getDefault().removeStickyEvent(new PurchaseEvent());也可以全部移除

  EventBus.getDefault().removeAllStickyEvents();还记得上面说的EventBus3.0通过注解的形式进行开发不影响性能吗?编译期进行注解解析肯定是要一个解析类,他就是

  EventBusAnnotationProcessor,来看看

  1,在build.gradle中添加配置

  apply plugin: com.neenbedankt.android-aptdepencies { compile org.greenrobot:eventbus:3.0.0 apt org.greenrobot:eventbus-annotation-processor:3.0.1}apt { arguments { eventBusIndex "com.whoislcj.eventbus.MyEventBusIndex" }}2,使用索引,此时编译一次,自动生成索引类在build\generated\source\apt\PakageName\

  EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();通过索引可以大大提升执行效率(注意,如果使用3.0中新增的索引特性,那么所有的事件修饰符要用public来进行修饰,否则会报以下错误

  Subscriber method must be public

  )

  EventBus的优点是可以实现组件间通信的解耦,缺点是需要一堆Event类。

  下面来说说EventBus3.0和EventBus2.X之间的差异

  1,编码

  EventBus2.X必须以onEvent打头,而且很有可能会拼错

  public void onEvent(PurchaseEvent event) {//事件在哪个线程发布出来的,onEvent就会在这个线程中运行, 同 @Subscribe(threadMode = ThreadMode.POSTING)}public void onEventMainThread(PurchaseEvent event) {// 不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,同 @Subscribe(threadMode = ThreadMode.MAIN)}public void onEventBackgroundThread(PurchaseEvent event) {//那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行,同 @Subscribe(threadMode = ThreadMode.BACKGROUND)}public void onEventAsync(PurchaseEvent event) {//使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync,同 @Subscribe(threadMode = ThreadMode.ASYNC)}EventBus3.0名字不受限制,而且可以直接通过ThreadMode来指定运行线程,并且可以定义事件优先级

  /** * 普通事件 * @param event */ @Subscribe(threadMode = ThreadMode.MAIN, priority = 100) public void onPurchaseEvent(PurchaseEvent event) { } /** * 粘性事件 * @param event */ @Subscribe(threadMode = ThreadMode.MAIN, priority = 100, sticky = true) public void onPurchaseEvent(PurchaseEvent event) { }EventBus2.X注册方式多种

  public void register(Object subscriber) {register(subscriber, false, 0);}public void register(Object subscriber, int priority) {register(subscriber, false, priority);}public void registerSticky(Object subscriber) {register(subscriber, true, 0);}public void registerSticky(Object subscriber, int priority) {register(subscriber, true, priority);}private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {...}而EventBus3.0注册方式单一

  public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }而最重要的一点当然是性能这一块,这点我已经在上面说过了。

  最后附上一张图

标签: 区块链