使用场景

由于所开发的Android项目是个老项目,EventBus使用的还是EventBus2,整个项目是插件化架构,不同插件使用的ClassLoader不同。插件1中有个onEvent方法,用来更新插件1中的一些信息。在插件2中发送一个EventBus消息更新插件1。在插件升级的时候(新老插件ClassLoader不同)偶现下述异常。

java.lang.IllegalArgumentException: Expected receiver of type
com.yuntao.plugin.order.DetailActivity, but got
com.yuntao.plugin.order.DetailActivity at java.lang.reflect.Method.invoke(Native Method) at
java.lang.reflect.Method.invoke(Method.java:372) at
de.greenrobot.event.EventBus.invokeSubscriber(EventBus.java:498) at
de.greenrobot.event.EventBus.postToSubscription(EventBus.java:429) at
de.greenrobot.event.EventBus.postSingleEventForEventType(EventBus.java:410) at
de.greenrobot.event.EventBus.postSingleEvent(EventBus.java:383) at
de.greenrobot.event.EventBus.post(EventBus.java:263) at
com.yuntao.plugin.PaymentActivity$2.onClick(PaymentActivity.java:141) at
android.view.View.performClick(View.java:4783) at
android.view.View$PerformClick.run(View.java:19887) at
android.os.Handler.handleCallback(Handler.java:739) at
android.os.Handler.dispatchMessage(Handler.java:95) at
android.os.Looper.loop(Looper.java:135) at
android.app.ActivityThread.main(ActivityThread.java:5290) at
java.lang.refle.... 

异常原因

看异常结果是在调用Method.invoke中抛出的。追到invoke方法查看

public Object invoke(Object receiver, Object... args)throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {return invoke(receiver, args, isAccessible());}private native Object invoke(Object receiver, Object[] args, boolean accessible)throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;

最终只是调用了一个Native方法。那看下方法的注释啥时候会抛出IllegalArgumentException异常

/**
@throws IllegalArgumentException* if the number of arguments doesn't match the number of parameters, the receiver* is incompatible with the declaring class, or an argument could not be unboxed* or converted by a widening conversion to the corresponding parameter type*/

翻译来看遇到下述情况:参数数目不同,接收的对象与声明的类不一致,参数不能自动拆箱,参数不能通过拓宽转换为相应的参数类型。
由于参数都是String类型,异常是Expected receiver of type ,基本可以断定是receiver与声明的类不一致的问题。
写一段测试代码看看:

public class TestReflect {public void show(Integer a) {Log.i("pyt", a + "");}
}public class TestReflect1 {public void show(Integer a) {Log.i("pyt", a + "");}
}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);try {Class<?> c = Class.forName("com.yuntao.testeventbus.TestReflect");Method method = c.getDeclaredMethod("show", Integer.class);method.setAccessible(true);method.invoke(new TestReflect1()); //正常传递c.newInstance(),这里换成另一个类的对象} catch (Exception e) {e.printStackTrace();Log.e("pyt", e.toString());}}

果然会抛出异常

 void invokeSubscriber(Subscription subscription, Object event) {try {subscription.subscriberMethod.method.invoke(subscription.subscriber, event);} catch (InvocationTargetException e) {handleSubscriberException(subscription, event, e.getCause());} catch (IllegalAccessException e) {throw new IllegalStateException("Unexpected exception", e);}}

EventBus2源码分析

按照method的声明类与invoke参数的对象声明类不一致会出现异常的思路来分析。
直接查看异常的调用栈,最终方法

 void invokeSubscriber(Subscription subscription, Object event) {try {subscription.subscriberMethod.method.invoke(subscription.subscriber, event);} catch (InvocationTargetException e) {handleSubscriberException(subscription, event, e.getCause());} catch (IllegalAccessException e) {throw new IllegalStateException("Unexpected exception", e);}}


看下两个类的关系Subscription里包含了EventBus注册的那个对象与onEvent方法。subscriber就是注册的实例,subscriberMethod中包含的就是当前实例的onEvent方法。上边的方法是直接取出method.invoke(subscription),都是保存在Subscription实例中的,那就要看Subscription实例是如何创建的了。

追溯到EventBus的register方法,查看

List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());

findSubscriberMethods方法

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {String key = subscriberClass.getName();List<SubscriberMethod> subscriberMethods;synchronized (methodCache) {subscriberMethods = methodCache.get(key);}if (subscriberMethods != null) {return subscriberMethods;}subscriberMethods = new ArrayList<SubscriberMethod>();省略n行代码,主要是反射当前subscriberClass中的onEvent方法,创建SubscriberMethod对象,缓存到methodCache(一个静态HashMap变量)中,key是类的name。
}        

看上述方法拿到了当前类的name,然后就会去methodCache中查找,有的话会直接返回。看到这里一定会联想到我们的框架,当有静态变量缓存的时候会出现的同名类转换异常问题(ClassLoader不同)。
当插件升级的时候,如果methodCache缓存了旧的插件的method,新插件获取到了旧的插件的method,然后invoke方法传入的是新插件的实例,这时候会出现异常。

上述分析比较简单,不懂可以详细参考EventBus源码

解决方案

更改EventBus源码。更改methodCache的key为Class对象,或者为Class对象的hashCode,这样可以使用SparseArray缓存,提高性能
在创建新的插件的时候调用EventBus的clearCaches方法清空缓存。

Android插件中使用EventBus出现java.lang.IllegalArgumentException: Expected receiver of type xxx, but got xx相关推荐

  1. 关于Servlet服务器中的 **Caused by: java.lang.IllegalArgumentException: servlet映射中的[servletDemo]无效**

    关于Servlet服务器中的 Caused by: java.lang.IllegalArgumentException: servlet映射中的[servletDemo]无效 错误信息如下:第三行 ...

  2. java.lang.IllegalArgumentException: Not an managed type

    出现异常: java.lang.IllegalArgumentException: Not an managed type spring+jpa框架整合出现问题: 从svn checkout项目出来后 ...

  3. Android 运行报错:Cause: java.lang.IllegalArgumentException

    Android 运行测试时,报了一个错: Cause: java.lang.IllegalArgumentException 解决办法: 在[Build]中Rebuild Project后,再运行. ...

  4. Android异常总结---1.异常原因: java.lang.IllegalArgumentException: URI: content://com.android.contacts/con

    1.异常原因: java.lang.IllegalArgumentException: URI: content://com.android.contacts/contacts1/data, call ...

  5. java.lang.IllegalArgumentException: Name for argument type [java.lang.Integer] not available异常

    这个异常的大致意思是:参数类型的名称[java.lang.Integer]不可用,并且在类文件中也找不到参数名称信息. 就是说前端发起的请求的参数和后台接收的参数对应不上,我们项目采用前后端分离前端采 ...

  6. java.lang.IllegalArgumentException: Could not resolve placeholder ‘XXX‘ in string value “${XXX}“;

    spring项目运行的时候提示这个错误.很奇怪的是这个项目是我自己从原来自己写的git上迁移到gitlab上的,yml并没有做任何手脚. 检查了一下yml的书写格式,换行等,都是正确的,网上搜这个问题 ...

  7. java.lang.IllegalArgumentException: Not a managed type: class com.** 解决方案

    springboot  添加jpa根据表反向生成实体类后,启动报错如标题,是因为实体类的目录不对,改为启动类所在目录下面的目录即可.记录备忘.

  8. android java.lang.IllegalArgumentException: Comparison method violates its general contract! 问题

    android  java.lang.IllegalArgumentException: Comparison method violates its general contract! 问题 jav ...

  9. Android <java.lang.ClassNotFoundException:Didn‘t find class ‘XXX‘ on path:DexPathList> 解决

    Android <java.lang.ClassNotFoundException:Didn't find class 'XXX' on path:DexPathList> 解决 如图:问 ...

最新文章

  1. CNN 的一些可视化方法
  2. C++中对象的构造顺序和析构顺序
  3. SD Price公式(例程,即Formula)
  4. 音视频技术开发周刊 | 162
  5. LeetCode每日一题: 搜索插入位置(No.35)
  6. 谷歌再修复已遭利用的两个高危 Chrome 0day
  7. 无人车前轮转向参数校准分析
  8. 矩阵卷积、矩阵相乘以及频域相乘之间的关系
  9. MS VS 2013下载和安装中文语言包教程
  10. linux eqep驱动框架,AM3352 数据表, 产品信息与支持 | TI.com.cn
  11. 利用计算机辅助管理档案说明,计算机辅助档案管理
  12. uniapp路由守卫
  13. 中国传统的节日(端午节)
  14. MySQL:排序(filesort)详细解析(8000字长文)
  15. 用Python读取照片拍摄的详细信息(拍摄时间、地址等)
  16. MATLAB批量处理.nii文件----批量.nii转为jpg格式
  17. 【Cucumber系列】Junit Test Runner和CucumberOptions
  18. 2018-10-8-Win10-使用-GHO-安装出现-UWP-软件打开闪退-应用商店无法安装软件
  19. 蒙特卡洛 股票 matlab,风险管理matlab蒙特卡洛模拟股票价格
  20. 2020年最新民生香港卡教程(只需要存5W)

热门文章

  1. src下创建java文件_Eclipse下maven项目创建src/main/java 源文件夹报错文件文件已存在问题...
  2. 理解对方暴露服务的对象 Ingress 和 Service
  3. 伯乐在线 android,伯乐在线博客
  4. python模拟登陆 pixiv
  5. 近期 0day exploit 满天飞,原来是神秘的以色列公司 Candiru 在捣鬼
  6. 论文阅读:A Taxonomy and Evaluation of Dense Light Field Depth Estimation Algorithms
  7. 【深度学习-CS231n】线性分类器和神经网络
  8. 微信支付(使用官方SDK,SpringBoot)
  9. 计算共形几何暑假课lecture4
  10. 魔兽世界9.0主播最多的服务器,斗鱼主播服务器分布揭秘!魔兽世界怀旧服精彩不间断...