30

Suppose you define android:onClick="doClick" in your Activity as

protected void doClick(View view) { }

The documentation states that

This name must correspond to a public method that takes exactly one parameter of type View.

This is a given requirement of the underlying Class.getMethod() method, which only finds public methods as the documentation states that it

Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.

So how is it possible, that this implementation, which should not work at all, works on some devices and emulators, while it doesn't work on others using the same API levels as well?

Onik
  • 19,396
  • 14
  • 68
  • 91
tynn
  • 38,113
  • 8
  • 108
  • 143
  • I got interested by the question, I may be completely wrong but I think that it works with `protected` due to the fact that if `getMethod()` doesn't find a corresponding method in a given class it continues searching recursively in the super class. – Suleyman May 29 '18 at 22:54
  • 1
    it shouldn't work ideally. I checked the source code and they do not make the method accessible, which is required to access non public methods using reflection, maybe the have different implementation of this in different API version – Suhaib Roomy May 29 '18 at 23:13
  • I'd take a look at the DeclaredOnClickListener method, and the differences between the framework and the support library implementations: https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/view/View.java#L5350 and: https://github.com/reverseengineeringer/com.twitter.android/blob/master/src/android/support/v7/app/AppCompatViewInflater$DeclaredOnClickListener.java – Miguel Beltran May 30 '18 at 07:51
  • 1
    @Miquel both call to `AppCompatViewInflater.DeclaredOnClickListener` of _27.1.1_ which looks like the latest framework implementation. – tynn May 30 '18 at 08:00
  • @SuhaibRoomy it isn't required to make a protected method accessible if the accessor is in a subclass of or in the same package as the method. I would guess that LieForBananas is correct, and the framework may be creating a runtime proxy of the View implementation, enabling it to call #onClick without reflection. The devices where it doesn't work may be trying to call it without subclassing (with reflection) which will fail without calling AccessibleObject#setAccessible. – Lucas Ross Jun 05 '18 at 15:32
  • What you are saying about protected method is right. But the view is not a subclass of your activity. It can never be. Also view is not calling the method directly. There is an inner static class DeclaredOnClickListener(which holds the reference of view) that actually calls the method, there is no way it can be subclass of your activity – Suhaib Roomy Jun 05 '18 at 17:51
  • @SuhaibRoomy, you're right. tynn, looks like [this user had the same problem](https://stackoverflow.com/questions/39354652/java-protected-method-is-callable-through-reflection-in-debug-but-not-in-release). – Lucas Ross Jun 06 '18 at 20:41
  • @tynn, I don't know what's going on, but it seems likely to come down to different reflection/AccessibleObject-related C++-language code for different platforms in Dalvik/ART. – Lucas Ross Jun 07 '18 at 15:48
  • _"So how is it possible, that this implementation, which should not work at all"_ What makes you think a protected method cannot be invoked via reflection? – Onik Jun 09 '18 at 21:01
  • @Onik the `getMethod()` method only finds public methods. I'm not assuming that calling the method later is not possible. I was merely wondering how the non-public method could be found. The answer is that the method is public anyhow. But only in some setups. – tynn Jun 10 '18 at 05:41

2 Answers2

0

As per "The Java™ Tutorials" : The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package

Regis
  • 11
  • 2
  • 2
    How does this answer relate to the question? Protected `doClick` method described in the question is not accessed from a class in the same package or a subclass; it's somehow accessed from within Android SDK classes. – arcquim Jun 08 '18 at 12:25
0

I debugged the particular implementations. The relevant portion of the code is within the Support Library using Class.getMethod().

As stated in the documentation, this method only finds public member methods and behaves correctly. For some reason, all modifiers of declared protected methods of the Activity (these are onCreate() and doClick()) are set to 1, which means these are actually public.

I could only observe this behavior creating the debug build with a Mac. So why this happens is still an open question, which I'm trying to find an answer to.

tynn
  • 38,113
  • 8
  • 108
  • 143