使用JNI从单独的线程调用静态Java方法

使用JNI从单独的线程调用静态Java方法,java,android,c,android-ndk,java-native-interface,Java,Android,C,Android Ndk,Java Native Interface,我试图在android中使用JNI来生成一个函数指针,我正在使用的本机库使用它来转发对java的调用 调用initializeStateController时,将使用pthread_create创建一个新线程,该线程在状态控制器的状态发生更改时调用函数指针 但是,当我尝试从C中的state\u exec调用GetStaticMethodID时,我得到以下错误: JNI DETECTED ERROR IN APPLICATION: jclass is an invalid local refere

我试图在android中使用JNI来生成一个函数指针,我正在使用的本机库使用它来转发对java的调用

调用
initializeStateController
时,将使用
pthread_create
创建一个新线程,该线程在状态控制器的状态发生更改时调用函数指针

但是,当我尝试从C中的
state\u exec
调用
GetStaticMethodID
时,我得到以下错误:

JNI DETECTED ERROR IN APPLICATION: jclass is an invalid local reference: 0xec500019 (0xdead4321) in call to GetStaticMethodID
这是我当前的代码:

C代码

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);
}

结果是
FindClass
方法只返回本地引用,而不是全局引用。为了使这项工作,你需要使它成为一个全球性的参考。您可以按如下所示执行此操作

// 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);
来自Android文档:

错误:错误地假设FindClass()返回全局引用

FindClass()返回本地引用。很多人都不这么认为。在没有类卸载的系统中(如Android),可以将jfieldID和jmethodID视为全局的。(它们实际上不是引用,但在具有类卸载的系统中也存在类似的生存期问题。)但jclass是引用,FindClass()返回本地引用。常见的错误模式是“静态jclass”。除非手动将本地引用转换为全局引用,否则代码将被破坏


我把我的答案贴在你的答案之前,因为我自己已经找到了答案,但是我很感激你的回答,我会把你的答案标记为正确答案,而不是我的。这里的
FindClass()
没有什么特别之处。返回引用的所有内容都返回本地引用,除了
NewGlobalRef()
NewWeakGlobalRef()
// 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);