Call static Java method from separate thread using JNI

半世苍凉 提交于 2021-01-27 18:44:28

问题


I'm trying to use JNI in android to make a function pointer that a native library I'm using uses forward it's call to java.

When initializeStateController is called, a new thread is made using pthread_create that calls the function pointer whenever the state of the State Controller changes.

However, when I try to call GetStaticMethodID from state_exec in C, I'm getting the following error:

JNI DETECTED ERROR IN APPLICATION: jclass is an invalid local reference: 0xec500019 (0xdead4321) in call to GetStaticMethodID

This is my current code:

C Code

state_controller *controller;
JavaVM* gJvm = NULL;
static jclass sc_class;

JNIEnv* getEnv() {
    JNIEnv *env;
    int status = (*gJvm)->GetEnv(gJvm, (void**)&env, JNI_VERSION_1_6);
    if(status < 0) {
         status = (*gJvm)->AttachCurrentThread(gJvm, &env, NULL);
        if(status < 0) {
            return NULL;
        }
    }
    return env;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) {
    gJvm = pjvm;
    JNIEnv *env = getEnv();

    sc_class = (*env)->FindClass(env, "com/my/package/StateController");

    return JNI_VERSION_1_6;
}

int state_exec(int state, int from) {
    JNIEnv *env = getEnv();
    jmethodID mid = (*env)->GetStaticMethodID(env, sc_class, "stateExec","(II)I");
    jint result = (*env)->CallStaticIntMethod(env, sc_class, mid, state, from);

    return (int) result;
}

// This part is unimportant.
// This is just where I hand the function pointer
// to the library to use.

JNIEXPORT void JNICALL 
Java_com_my_package_StateController_initializeStateController
(
        JNIEnv *env,
        jobject jobj
) {
    controller = new_state_controller(
        state_exec
    );
    sc_start_controller(controller);
}

Java

package com.my.package;

class StateController {

    public native void initializeStateController();

    public static int stateExec(int state, int from) {
        Log.d("StateTest", "State " + state + " -> " + from);
        return 0;
    }
}

. . . 

(new StateController()).initializeStateController();

回答1:


It turns out that the FindClass method only returns local references, not global. In order to make this work you need to make it a global reference. You can do this as demonstrated below.

// Replace

    sc_class = (*env)->FindClass(env, "com/my/package/StateController");

// With

    sc_class = (*env)->NewGlobalRef(
        env, 
        (*env)->FindClass(
            env, 
            "com/my/package/StateController"
        )
    );

// Later when you are done with the class reference, run this to free it.

    (*env)->DeleteGlobalRef(env, sc_class);

From Android Docs:

Bug: Mistakenly assuming FindClass() returns global references

FindClass() returns local references. Many people assume otherwise. In a system without class unloading (like Android), you can treat jfieldID and jmethodID as if they were global. (They’re not actually references, but in a system with class unloading there are similar lifetime issues.) But jclass is a reference, and FindClass() returns local references. A common bug pattern is “static jclass”. Unless you’re manually turning your local references into global references, your code is broken.

https://android-developers.googleblog.com/2011/11/jni-local-reference-changes-in-ics.html



来源:https://stackoverflow.com/questions/46332147/call-static-java-method-from-separate-thread-using-jni

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!