3

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)
Scott
  • 3,663
  • 8
  • 33
  • 56
  • A `DeclaredOnClickListener` is created for each `View` for which you've specified an `onClick` attribute in the layout XML. Without the message from the `IllegalStateException`, or a more specific stack trace, it's hard to say what exactly is the problem. Could be the declared method wasn't found for whatever reason, something in that method threw another Exception when it was invoked, etc. That'd be where to start looking, though. – Mike M. May 19 '17 at 18:24
  • Not sure how I missed this before, but the `InvocationTargetException` at the bottom indicates that something in one of your `onClick` methods is throwing another Exception. That is, it's not that the method couldn't be found or accessed, but something inside the method is failing. Is that all of the stack trace you got? – Mike M. May 19 '17 at 23:22
  • That's all I see on the Play Store Developer Console. If there's somewhere to find more, I don't know it. – Scott May 20 '17 at 00:39
  • Hi Scott I am facing exactly the same problem and I am either unable to reproduce it any of my devices or emulators. Did you find the cause of the problem or a workaround? Thanks! – Spirrow Nov 20 '17 at 08:56

3 Answers3

5

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.

Shawn
  • 402
  • 4
  • 17
  • I love long answers, thank you! I will look for onClicks in my XML. It hadn't occurred to me that the issue might be there instead of in the java. I'm not sure why I can't see the error message - all Google is giving me is the error itself, not any of the other logging. – Scott May 19 '17 at 21:35
  • Note that I am not necessarily saying the problem is in the XML, it may be that you need to rename your click listener entirely in both the xml and java code to avoid clashes with some other method. – Shawn May 19 '17 at 21:42
  • I really appreciate your answer, but I may not be able to solve this. All of my onClick calls are to public methods. They are all unique names with only one implementation. And i haven't been able to replicate the problem myself. – Scott May 20 '17 at 04:36
  • It looks like this can also come from throwing an exception within the method: https://stackoverflow.com/questions/6020719/what-could-cause-java-lang-reflect-invocationtargetexception . So if one of your click listeners throws a null pointer exception or something perhaps that could cause it? – Shawn May 20 '17 at 21:08
0

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.

0

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.

Steve.e
  • 1
  • 3