Java protected method is callable through reflecti

2019-06-24 02:54发布

问题:

I just fixed a curious bug, but I am not sure how it was working in the first place. I am using a TinyBus even bus in my app. Please consider the following code:

import android.support.v4.app.Fragment;

public class MainFragment extents Fragment {
    ...

    //Event bus annotation
    @Subscribe
    protected void startTutorial(StartTutorial startTutorialEvent) {
        //show tutorial
    }

    ...
}

MainFragment is wired up to receive events properly. I wouldn't expect the above code to work though, because the #startTutorial method is declared protected. This is an except from code TinyBus uses to register the methods with @Subscribe annotation:

public ObjectsMeta(Object obj) {
    final Method[] methods = obj.getClass().getMethods();
    ..
    Subscribe ann;
    for (Method method : methods) {
        ...
        ann = method.getAnnotation(Subscribe.class);
        if (ann != null) {
        ...
        }
    }
}

Full source code is here. As we can see, it uses #getMethods to get all the class methods to iterate over and find ones with relevant annotation. Now according to Java docs, #getMethods does the following:

Returns an array containing Method objects reflecting all the public member methods of the class or interface represented by this Class object, including those declared by the class or interface and those inherited from superclasses and superinterfaces. Array classes return all the (public) member methods inherited from the Object class. The elements in the array returned are not sorted and are not in any particular order. This method returns an array of length 0 if this Class object represents a class or interface that has no public member methods, or if this Class object represents a primitive type or void.

Here's the source.

It's pretty clear that it should only return an array or public methods.

Now with all that background out of the way, here's the question: how come my MainFragment code worked when my app was build for debugging but stopped working when it was build for production? I was able to reproduce it by making my build.gradle file look like this:

buildTypes {
    debug {
        minifyEnabled true
        proguardFile 'proguard-rules.pro'
        debuggable false 
        shrinkResources true
    }
    ...
}

Once the app was installed, it no longer worked. Changing the accessor of MainFragment#startTutorial to public fixed it in locally and in production. Any ideas? Thanks!