AndroidEventBus icon indicating copy to clipboard operation
AndroidEventBus copied to clipboard

我使用eventbus传递点击事件,出现了订阅者执行俩次的问题

Open chengzhijun0706 opened this issue 9 years ago • 8 comments

image 传递的参数中的对象有一个字段是list

chengzhijun0706 avatar Dec 28 '16 05:12 chengzhijun0706

我打断点看,发送事件的位置只发送了一遍,但是接收事件的位置出现了俩次

chengzhijun0706 avatar Dec 28 '16 05:12 chengzhijun0706

我收到三次。。

ccclll1990 avatar Jan 17 '17 04:01 ccclll1990

@ccclll1990 问题的原因找到了,是fragment的复用引起的,如果fragment要复用就要进行特殊的处理

chengzhijun0706 avatar Jan 17 '17 04:01 chengzhijun0706

还有种情况,如果传的参数是Map类型,还会执行N次(N>5),看源码是Map继承自多个类的子类,然后符合这些类的响应,自然就执行多次了,解决办法是只传基础类型数据,int,boolean,string及数组类型,或者改源码

creky avatar Mar 09 '18 01:03 creky

@Creky 我也遇见这个问题了,我直接传递父类的直接子类就解决了这个问题,谢谢了哈。

yangkun19921001 avatar Mar 11 '18 10:03 yangkun19921001

@chengzhijun0706 你好,我也是fragment出现的,怎么处理的?

xilost avatar Dec 29 '18 03:12 xilost

@chengzhijun0706 你好,我也是fragment出现的,怎么处理的?

如果fragment存在复用的场景需要特殊处理,我是这样处理的,仅供参考(前提时发布事件的时候能够取到订阅的对象) 需要对源码进行改动 为了区分不同的fragment的对象,我想把fragment对象的hash作为tag,进行事件注册。 但是在注解中又只能使用常量。所以想了一个迂回的办法。 1.注解的时候用参数标识这个事件注册的时候需要把对象object的hash加到tag中, 2.在EventBus注册的时候识别这个参数,并把object的hash加到tag中 3.事件发布的时候将fragment对象的hash手动添加到tag中

1.首先Subscriber注解中增加一个方法 /** */ boolean registerWithObjectHash() default false; 2.Eventbus注册的时候判断该参数,并注册的时候特殊处理 浏览代码容易找到Eventus注册的方法是在SubsciberMethodHunter. findSubcribeMethods 的方法中,对该方法略作改动

/**
     * 查找订阅对象中的所有订阅函数,订阅函数的参数只能有一个.找到订阅函数之后构建Subscription存储到Map中
     * 
     * @param subscriber 订阅对象
     * @return
     */
    public void findSubcribeMethods(Object subscriber) {
        if (mSubcriberMap == null) {
            throw new NullPointerException("the mSubcriberMap is null. ");
        }
        Class<?> clazz = subscriber.getClass();
        // 查找类中符合要求的注册方法,直到Object类
        while (clazz != null && !isSystemCalss(clazz.getName())) {
            final Method[] allMethods = clazz.getDeclaredMethods();
            for (int i = 0; i < allMethods.length; i++) {
                Method method = allMethods[i];
                // 根据注解来解析函数
                Subscriber annotation = method.getAnnotation(Subscriber.class);
                if (annotation != null) {
                    // 获取方法参数
                    Class<?>[] paramsTypeClass = method.getParameterTypes();
                    // 订阅函数只支持一个参数
                    if (paramsTypeClass != null && paramsTypeClass.length == 1) {
                        Class<?> paramType = convertType(paramsTypeClass[0]);
                        EventType eventType 
                        if(annotation.registerWithObjectHash()){
                        eventType = new EventType(paramType, annotation.tag()+subscriber.toString());   
                        }else{
                         eventType = new EventType(paramType, annotation.tag());
                        }
                        TargetMethod subscribeMethod = new TargetMethod(method, eventType,
                                annotation.mode());
                        subscibe(eventType, subscribeMethod, subscriber);
                    }
                }
            } // end for
              // 获取父类,以继续查找父类中符合要求的方法
            clazz = clazz.getSuperclass();
        }
    }

example:

注册时

class Fragment
...

@Subscriber(tag="testevent",registerWithObjectHash=true)
public void test(String test){
}

发布事件时

post("","testevent"+fragment.toString)

maruiwhu avatar Dec 29 '18 06:12 maruiwhu

@xilost 我用了一个比较笨的办法,就是在你传递的对象中携带有一个区分页面的字段,在接受的地方判断这个字段的值是否和本页面相等就好了

chengzhijun0706 avatar Dec 29 '18 12:12 chengzhijun0706