Search code examples
javaandroidjava-native-interface

Android JNI GetMethodID fails to find a method in release build


I am calling a java method from C++ in android app. Works fine in debug but GetMethodID fails in release build.

// Java
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...
        showKeyboard();
    }
    public void showKeyboard()
    {
        Log.i("my_app", "MainActivity::showKeyboard");
        //...
    }
}

// C++
JavaVM* gJvm; // Saved in JNI_OnLoad()
jobject gMainActivity; // Saved in call Java -> C++
                       // using JNIEnv->NewGlobalRef(mainActivity)
void my_thread_func() {
    JNIEnv* jniEnv = nullptr;
    gJvm->AttachCurrentThread(&jniEnv, nullptr);;
    jclass activityClass = jniEnv->GetObjectClass(gMainActivity);
    jmethodID methodId = jniEnv->GetMethodID(activityClass, "showKeyboard", "()V");
    // Debug build: methodId is valid
    // Release build: methodId is null
}

showKeyboard is called from onCreate only for debug purposes. I can see the line "MainActivity::showKeyboard" in the output. Also it works as expected in debug build.

I suspect java optimization kicks in and removes the method in release. Although it's called in onCreate, it may just get inlined. I tried to add 50+ lines of log prints hoping to discourage inlining but it didn't change anything.

Is there a way to disable java optimization for this method? Or how else can I troubleshoot it?


Solution

  • Yes, compiling code in release mode turns on Proguard minification and obfuscation. If you look at the documentation, you will find several options:

    1. Use the @Keep annotation on the showKeyboard method. This will get picked up by the default proguard ruleset and prevent the method from being renamed or removed.
    2. Write your own Proguard rules to keep the method. I think this ought to look like:
    -keep public class com.example.MainActivity {
        public void showKeyboard();
    }