我的一个应用程序获取了一些 IllegalStateException 的崩溃报告。 Stack Traces 说它来自 android.view.View$DeclaredOnClickListener.onClick(view)。我在测试或日常使用中从未遇到过这个错误(我自己每天都在运行 Android 6.0.1 的三星 Note 4 上使用 app)。老实说,我不知道从哪里开始寻找,因为 Stack Trace 似乎甚至没有提到我自己的任何代码,只是平台代码。我错过了什么?此版本确实使用了支持库,但不使用 fragment ,这是此错误的其他解决方案所指的地方。
下面我粘贴了其中一个堆栈跟踪。这是来自运行 Android 6.0 的 Moto G Turbo
java.lang.IllegalStateException:
at android.view.View$DeclaredOnClickListener.onClick(View.java:4455)
at android.view.View.performClick(View.java:5201)
at android.view.View$PerformClick.run(View.java:21163)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method:0)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Caused by: java.lang.reflect.InvocationTargetException:
at java.lang.reflect.Method.invoke(Native Method:0)
at android.view.View$DeclaredOnClickListener.onClick(View.java:4450)
最佳答案
抱歉回答太长,但我认为解释如何深入 Android 框架来调试问题很有用。
可以在此处访问引发此异常的代码:
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/View.java
4452 try {
4453 mMethod.invoke(mHostView.getContext(), v);
4454 } catch (IllegalAccessException e) {
4455 throw new IllegalStateException(
4456 "Could not execute non-public method for android:onClick", e);
4457 } catch (InvocationTargetException e) {
4458 throw new IllegalStateException(
4459 "Could not execute method for android:onClick", e);
4460 }
反射及其在点击监听器中的使用
基本上它正在做的是使用反射调用基于字符串的方法。此方法是由应用程序开发人员定义的一种方法,用于响应单击的按钮。这通常是因为您可以通过 XML 指定 onClickListener 方法,例如您可以说 invoke “goDoWhatever”而不是通常的“onClick”。反射采用此方法的字符串表示形式,并尝试调用该名称的指定类上的方法。
当所需的方法不存在时会发生反射错误,例如,如果名称错误,方法是私有(private)的,或者参数不同。
请注意,在这种情况下有两种不同的异常,一种用于非公共(public)方法,另一种用于无法执行它。我不知道为什么您的堆栈跟踪没有与 IllegalStateException 关联的消息,但制造商可以根据需要修改此代码。
我怀疑您有一个正确名称的方法,因为如果名称错误,解析方法函数会引发不同的错误:
4463 @NonNull
4464 private Method resolveMethod(@Nullable Context context, @NonNull String name) {
4465 while (context != null) {
4466 try {
4467 if (!context.isRestricted()) {
4468 return context.getClass().getMethod(mMethodName, View.class);
4469 }
...
4485 throw new IllegalStateException("Could not find method " + mMethodName
4486 + "(View) in a parent or ancestor Context for android:onClick "
4487 + "attribute defined on view " + mHostView.getClass() + idText);
4488 }
4489 }
所以这给我们留下了我能想到的两种可能性:它找到的方法签名错误,或者它找到的方法是静态/私有(private)的。
我将如何调试这个:
我猜您在 xml 中的某处指定了一个点击监听器(在您的 xml 文件中查找“android:onClick=”。然后在您的应用程序中搜索所有同名的方法,并确保它们将单个 View 作为参数(确保在文件中也导入“android.view.View”,因为导入错误的 View 可能会导致此问题。还要确保它们不是静态的。也可能值得寻找私有(private)的东西,因为这也会导致问题但根据您的堆栈跟踪,它似乎不太可能。
为什么这个问题很难重现:
如果您注意到android框架中的“resolveMethod”方法只是返回同名的第一个方法,那么拥有一个类是有效的java:
class Foo{
void bar(String s){}
void bar(View s){}
方法签名由名称(例如“bar”)和参数列表组成(在这种情况下,是包含一项“View”的列表或包含一项“String”的列表)。
此 Android 框架代码可能会找到“void bar(String s)”而不是“void bar(View s)”。这可能会导致难以重现的错误,因为反射查找方法的顺序是不确定的( Java reflection: Is the order of class fields and methods standardized? )。因此,您可能很难重现它,因为特定设备可能会以某种方式确定性地迭代它们,但不一定与其他设备/实现的方式相同。
我希望这有帮助!请让我知道结果如何,我是一名研究生,正在研究诸如此类的软件缺陷,因此详细信息对我非常有用。
关于java - Play 商店崩溃报告 : IllegalStateException on android. view.View$DeclaredOnClickListener.onClick,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44073861/