问题
One of my apps picked up some crash reports for IllegalStateException. The Stack Traces say it's coming from android.view.View$DeclaredOnClickListener.onClick(view). I have never come across this error in my testing or daily use (I use the app myself on a daily basis on a Samsung Note 4 running Android 6.0.1). Honestly I don't know where to begin to look because the Stack Trace doesn't seem to even refer to any of my own code, just platform code. What am I missing? This version does use the support library, but not fragments, which is where other solutions to this error have been referring to.
Below I've pasted one of the Stack Traces. This is from a Moto G Turbo running Android 6.0
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)
回答1:
Sorry for the long answer but I think its useful to explain how to dig into the Android framework to debug the problem.
The code that throws this exception can be accessed here:
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 }
Reflection and its use in click listeners
Basically what it is doing is invoking a method based on a string using reflection. This method is one that is defined by an app developer to respond to a button being clicked. This is commonly done since you can specify an onClickListener method via XML, for example you can say invoke "goDoWhatever" instead of the usual "onClick". Reflection takes a string representation of this method and tries to call a method on a specified class of that name.
A reflection error occurs when the desired method does not exist, for example if you get the name wrong, the method is private, or the parameters are different.
Note that in this case there are two different exceptions, one for a non public method and then another for not being able to execute it. I don't know why your stack trace doesn't have a message associated with the IllegalStateException but manufacturers can modify this code if they desire.
I suspect that you have a method of the correct name since the resolve method function throws a different error if the name is wrong:
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 }
So this leaves us with two possibilities that I can think of: the method it finds has the wrong signature or the method it finds is static/private.
How I would go about debugging this:
I am guessing you specify a click listener in your xml somewhere (look for "android:onClick=" in your xml files. Then search your application for all methods of the same name and make sure that they take a single View as a parameter (make sure you import "android.view.View" in the file too as importing the wrong view could cause this). Also make sure they are not static. It is also probably worth looking for things that are private as this can also cause problems but based on your stack trace it seems less likely.
Why this problem may be hard to reproduce:
If you notice the method "resolveMethod" in the android framework simply returns the first method of the same name, so whereas it is valid java to have a class:
class Foo{
void bar(String s){}
void bar(View s){}
A method signature consists of a name (such as "bar") and a list of parameters, (in this case a list with one item "View" or a list with one item "String").
This Android framework code might find "void bar(String s)" instead of "void bar(View s)". This can cause a hard to reproduce bug since the order in which reflection finds methods is non deterministic (Java reflection: Is the order of class fields and methods standardized?). So you may have a hard time reproducing it since a specific device probably deterministically iterates through them in some way but not neccessarily the same way as some other device/implementation.
I hope this helps! Please let me know how it turns out, I am a graduate student doing research on software defects such as this one so details are quite useful to me.
回答2:
I recently received the same kind of error reports for one of my two programs, and these crashes occurred on Android 6.0.1 and 7.1. The affected app had been in the store for many months, but this phenomenon was totally new to me and repeated within a few days with is quite improbable from a statistic point of view.
However, FrearTheCron's explanations have been extremely helpful, but are finally not satisfying. First, the "illegal state exception" should have appeared in line 4455, but line 4455 handles an "IllegalAccessException", while the first one is handled in line 4458. This is a contradiction which I do not have any explanation for. Maybe it's a bug in the Java VM.
Further, I checked all "android:onClick" entries and callbacks in my app, and all of these have rather unique names and thus do not have variants with same names and different parameters.
These examinations make me think that the problem is not caused by a coding bug in the app, but by an Android misbehaviour.
But how could this happen, and how could this be avoided? One of my apps uses two activities, while the other one uses only one, but with fragments. Only the app with two activities is affected. My theory is that Android gets confused with the views and activities, possibly triggered by some strange pause/stop/destroy/create/start/resume activity state change pattern, and tries to associate the view to the wrong activity. For example the view of my UM Player shows the tracks of an album, the system detects a click on a track, but this click is sent to the other activity, which shows the albums on the device and cannot handle the click-to-track callback. The result would be as described in the crash report.
Thus I am going to change my app in that way that both activities have callbacks for each "android::onClick" and will ignore calls sent to the wrong handler.
Maybe this helps. Any further light in the darkness would be highly appreciated.
回答3:
I had an identical error from the play store. There is certainly not enough info to quickly point to a problem. FearTheCron's answer led me to look at my xml for android:onClick statements. I have 6 statements and didn't see any problems at first, then I noticed I had inadvertently created a setOnClickListener as well for one of them. I didn't see any problems in my tests on four different phones and tablets over a month of use. I removed the setOnClickListener. I'm not sure this will fix the problem, but I should have been more careful.
来源:https://stackoverflow.com/questions/44073861/play-store-crash-report-illegalstateexception-on-android-view-viewdeclaredoncl