解决EventBus在子类重写父类2个订阅函数时Crash

摘要

解决:EventBus在子类重写父类2个订阅函数时Crash。参考issure。

由于在重复注册订阅方法时,在第三次注册时没有把method包裹成FindState,导致第四次注册订阅方法时走到了错误的逻辑里面,引起本次的崩溃!

准备

父类:

open class EventBusBaseFragment : Fragment() {
    @Subscribe
    open fun subscribeMessage0(message: SendMessage) {
        Log.d("EventBusBaseFragment", "EventBusBaseFragment subscribeMessage0")
    }

    @Subscribe
    open fun subscribeMessage1(message: SendMessage) {
        Log.d("EventBusBaseFragment", "EventBusBaseFragment subscribeMessage1")
    }
}

子类:

open class SendFragment : EventBusBaseFragment() {
    @Subscribe
    override fun subscribeMessage0(message: SendMessage) {
        Log.d("SendFragment", "SendFragment subscribeMessage0")
    }

    @Subscribe
    override fun subscribeMessage1(message: SendMessage) {
        Log.d("SendFragment", "SendFragment subscribeMessage1")
    }
}

EventBus的关键代码:

// /org/greenrobot/eventbus/SubscriberMethodFinder.java
boolean checkAdd(Method method, Class eventType) {
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        return true;
    } else {
        if (existing instanceof Method) {
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                throw new IllegalStateException();
            }
            anyMethodByEventType.put(eventType, this);
        }
        return checkAddWithMethodSignature(method, eventType);
    }
}
// /org/greenrobot/eventbus/SubscriberMethodFinder.java
private boolean checkAddWithMethodSignature(Method method, Class eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    String methodKey = methodKeyBuilder.toString();
    Class methodClass = method.getDeclaringClass();
    Class methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
        if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
            return true;
        } else {
            subscriberClassByMethodKey.put(methodKey, methodClassOld);
            return false;
        }
    }
}

调查

第一次:注册SendFragment#subscribeMessage0

关键变量:

methodClass:com.abelhu.androidstudy.ui.send.SendFragment
methodClassOld:null
methodKey:subscribeMessage0>com.abelhu.androidstudy.message.SendMessage

结果:method直接插入anyMethodByEventType,此时anyMethodByEventType里存放的是method

第二次:注册SendFragment#subscribeMessage1

关键变量:

methodClass:com.abelhu.androidstudy.ui.send.SendFragment
methodClassOld:null
methodKey:subscribeMessage1>com.abelhu.androidstudy.message.SendMessage

结果:

  1. anyMethodByEventType直接插入本次method
  2. eventType相同,拿到第一次的method
  3. checkAddWithMethodSignature的时候,因为方法名不一样,本次订阅被当做有效值,记录到subscriberClassByMethodKey
  4. 最后把包裹methodFindState插入anyMethodByEventType,此时anyMethodByEventType里存放的是FindState

第三次:EventBusBaseFragment#subscribeMessage0

关键变量:

methodClass:com.abelhu.androidstudy.ui.send.EventBusBaseFragment
methodClassOld:com.abelhu.androidstudy.ui.send.SendFragment
methodKey:subscribeMessage0>com.abelhu.androidstudy.message.SendMessage

结果:

  1. anyMethodByEventType直接插入本次method
  2. eventType相同,拿到第二次的FindState
  3. 做检查时,FindState不是method 注意:这一步没有把本次的method包裹成FindState
  4. checkAddWithMethodSignature中,先把本次订阅插入,在将原先的订阅插入,作为替换

第四次:EventBusBaseFragment#subscribeMessage1

关键变量:

methodClass:com.abelhu.androidstudy.ui.send.EventBusBaseFragment
methodClassOld:com.abelhu.androidstudy.ui.send.SendFragment
methodKey:subscribeMessage0>com.abelhu.androidstudy.message.SendMessage

结果:

  1. anyMethodByEventType直接插入本次method
  2. eventType相同,拿到第二次的method 注意:这就是引起Crash的位置
  3. 做检查时,FindStatemethod
  4. 最终引起IllegalStateException

结论

由于在重复注册订阅方法时,在第三次注册时没有把method包裹成FindState,导致第四次注册订阅方法时走到了错误的逻辑里面,引起本次的崩溃!

解决

解决方法很简单了,只需要把anyMethodByEventType.put(eventType, this);挪动一下位置,放到return的上方,让所有的重复注册都把method包裹成FindState,就可以了,代码如下:

// /org/greenrobot/eventbus/SubscriberMethodFinder.java
boolean checkAdd(Method method, Class eventType) {
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        return true;
    } else {
        if (existing instanceof Method) {
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                // Paranoia check
                throw new IllegalStateException();
            }
        }
        // Put any non-Method object to "consume" the existing Method
        anyMethodByEventType.put(eventType, this); // 注意:这一行被移动了一下位置
        return checkAddWithMethodSignature(method, eventType);
    }
}

参考资料

  1. kakaxicm的github
  2. EventBus的issue
  3. kakaxicm关于EventBus的介绍一
  4. kakaxicm关于EventBus的介绍二

你可能感兴趣的:(解决EventBus在子类重写父类2个订阅函数时Crash)