博客
关于我
EventBus3.0注意事项
阅读量:217 次
发布时间:2019-02-28

本文共 5891 字,大约阅读时间需要 19 分钟。

1、父类中定义的订阅方法不能被重写

比如你在父类中定义了一个订阅方法 

@Subscribe(threadMode = ThreadMode.MAIN)public void onEvent(Item item) {}

那么不能在子类中重写该方法,否则一旦我们调用EventBus.getDefault().regist(this)后,EventBus就会查询当前类及其父类中的订阅方法,如果发现父类中也订阅了相同的订阅方法(被子类override了),则会抛出异常,源码参见:

SubscriberMethodFinder.FindState类中的checkAdd();

boolean checkAdd(Method method, Class<?> eventType) {Object existing = anyMethodByEventType.put(eventType, method);if (existing == null) {    return true;} else {    if (existing instanceof Method) {    //检测被子类override的方法并抛出异常,如果子类覆写了父类中的订阅方法,则在查找父类中的同名方法时,会抛出异常。        if (!checkAddWithMethodSignature((Method) existing, eventType)) {            throw new IllegalStateException();        }        anyMethodByEventType.put(eventType, this);    }    return checkAddWithMethodSignature(method, eventType);}}

2、订阅方法的要求

1)修饰符必须为pubic, 非static,非abstract

2)方法参数必须只能有一个,不能没有或有多个参数

原因参见SubscriberMethodFinder类中的findUsingReflectionInSingleClass方法

private void findUsingReflectionInSingleClass(FindState findState) {Method[] methods;try {//获取当前类中的所有方法methods = findState.clazz.getDeclaredMethods();} catch (Throwable th) {// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149methods = findState.clazz.getMethods();findState.skipSuperClasses = true;}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) { //带有Subscribe注解//其他代码省略}} 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)) {//不合法的注解方法 (必须为public,非static、非abstract)String methodName = method.getDeclaringClass().getName() + "." + method.getName();throw new EventBusException(methodName +" is a illegal @Subscribe method: must be public, non-static, and non-abstract");}}}


3、关于订阅事件

什么是订阅事件,其实就是订阅方法的参数

EventBus用于订阅和发送事件,在EventBus类中,定义了一个HashMap,用于保存事件及其订阅方法:

//key为订阅事件Clas,value为该事件的订阅者,可能有多个private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

发送事件的时候,拿到该订阅事件的所有订阅方法,依次发送

 

Class<?> eventClass;//订阅事件CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) {//拿到订阅该事件的所有方法subscriptions = subscriptionsByEventType.get(eventClass);}if (subscriptions != null && !subscriptions.isEmpty()) {for (Subscription subscription : subscriptions) {//依次反射调用订阅方法}}

事件本身是个Java对象,那么就可以发生继承和接口的实现,比如我们定义了

 

public interface EventInterface{}public class FatherEvent implements EventInterface{}public class ChildEvent extends FatherEvent{}

ChildEvent继承FatherEvent,FatherEvent实现了EventInterface接口。

 


默认情况下,EventBus是支持事件继承的订阅的,怎么理解呢。

 

比如我们订阅了如下三个方法:

@Subscribe(threadMode = ThreadMode.MAIN)public void onEventFather(FatherEvent fatherEvent){}@Subscribe(threadMode = ThreadMode.MAIN)public void onEventChild(ChildEvent childEvent){}@Subscribe(threadMode = ThreadMode.MAIN)public void onEventInterface(EventInterface eventInterface){}

a)当我们调用 EventBus.getDefault().post(new ChildEvent()); 事件时,上面三个方法都可以监听到。

因为ChildEvent继承FatherEvent,FatherEvent又实现了EventInterface接口。

b)当我们调用 EventBus.getDefault().post(new FatherEvent()); 事件时,则订阅了FatherEvent和EventInterface接口的方法可以监听到。

因为FatherEvent实现了EventInterface。

也就是说,发送的事件时,如果该事件的父类或实现的接口被订阅,则也可以监听到。

4、事件过滤

EventBus是基于事件的,也就是订阅方法参数,用户要关心不同的事件,就需要定义不同的事件对象。

(与BrocastReceiver相比,EventBus中的事件就相当于BrocastReceiver中的action)

比如我们需要监听用户模块的登录和退出操作,就需要定义两个Event:LoginEvent和LoginOutEvent

然而实际项目中,可能我们需要监听很多的事件,就需要定义很多的事件类(然而大部分事件都只是为了区分,并没有业务实现),很多网友就想按照BrocastReceiver的方式去监听指定动作的事件(只有一个Intent,用action来区分事件),从而减少事件类的定义。

这虽然违背了观察者模式的意图,但是冗余事件类的定义,我个人也不是太喜欢,有网友提出在事件类中定义一个action来区分,根据action来区分。比如

@Subscribe(threadMode = ThreadMode.MAIN)public void onLoginEvent(UserEvent event) {String action = event.getAction();if(action.equals("login")){//监听到登录操作}else if(action.equals("loginout")){//监听到退出操作操作}}@Subscribe(threadMode = ThreadMode.MAIN)public void onInfoChangeEvent(UserEvent event) {String action = event.getAction();if(action.equals("infoChange")){//监听到用户资料修改操作}}

这种方式实现,这样可以减少部分Event的定义,但是需要我们在方法中自己匹配action。如果能像BrocastReceiver那样注册自己想要的动作就方便很多。本人针对这种情况

做了个简单处理,已把代码方法gitbub上,链接 

大致使用流程就是,在注解Subscribe注解中添加了一个action的数组,用于指定监听动作

@Subscribe@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Subscribe {ThreadMode threadMode() default ThreadMode.POSTING;boolean sticky() default false;int priority() default 0;/*** 监听动作,很多网友多有这样的需求,可以想广播那样监听指定动作的事件* @note: 设置了actions,事件必须为PostEvent及其子类,当发送事件时,会拿到PostEvent中的action进行匹配* @return*/String[] actions() default {};}

注册事件

//监听多个动作@Subscribe(threadMode = ThreadMode.MAIN,actions = {EventAction.ACTION,EventAction.ACTION2})public void onEventAction(PostEvent item) {}//监听1个动作@Subscribe(threadMode = ThreadMode.ASYNC,actions = {EventAction.ACTION2})public void onEventAction2(PostEvent item) {}

发送事件,注意事件必须为PostEvent及其子类。

 

EventBus.getDefault().post(new PostEvent(EventAction.ACTION));

5、其他问题

1)混淆

-keepattributes *Annotation*-keepclassmembers class ** {@org.greenrobot.eventbus.Subscribe <methods>;}-keep enum org.greenrobot.eventbus.ThreadMode { *; }# Only required if you use AsyncExecutor-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {<init>(Java.lang.Throwable);}

链接:

2)不支持跨进程

目前EventBus只支持跨线程,而不支持跨进程。如果一个app的service起到了另一个进程中,那么注册监听的模块则会收不到另一个进程的EventBus发出的事件。

3)为了防止多次注册,可以在注册前,先判断一下:

if (!EventBus.getDefault().isRegistered(this)) {    EventBus.getDefault().register(this);}

 

转载地址:http://cles.baihongyu.com/

你可能感兴趣的文章
MySQL5.6忘记root密码(win平台)
查看>>
MySQL5.6的Linux安装shell脚本之二进制安装(一)
查看>>
MySQL5.6的zip包安装教程
查看>>
mysql5.7 for windows_MySQL 5.7 for Windows 解压缩版配置安装
查看>>
Webpack 基本环境搭建
查看>>
mysql5.7 安装版 表不能输入汉字解决方案
查看>>
MySQL5.7.18主从复制搭建(一主一从)
查看>>
MySQL5.7.19-win64安装启动
查看>>
mysql5.7.19安装图解_mysql5.7.19 winx64解压缩版安装配置教程
查看>>
MySQL5.7.37windows解压版的安装使用
查看>>
mysql5.7免费下载地址
查看>>
mysql5.7命令总结
查看>>
mysql5.7安装
查看>>
mysql5.7性能调优my.ini
查看>>
MySQL5.7新增Performance Schema表
查看>>
Mysql5.7深入学习 1.MySQL 5.7 中的新增功能
查看>>
Webpack 之 basic chunk graph
查看>>
Mysql5.7版本单机版my.cnf配置文件
查看>>
mysql5.7的安装和Navicat的安装
查看>>
mysql5.7示例数据库_Linux MySQL5.7多实例数据库配置
查看>>