2017-09-20 75 views
1

我想在android中使用JNI来创建一个函数指针,我正在使用的本地库使用它向前调用java。使用JNI从单独的线程调用静态Java方法

当调用initializeStateController时,使用pthread_create创建一个新线程,每当状态控制器的状态发生变化时调用函数指针。

然而,当我尝试调用GetStaticMethodID从用C state_exec,我发现了以下错误:

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

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

事实证明,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的文档:

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

+0

我贴我的答案之前,你的权利,我已经想通了我自己,但我很欣赏的反应,我还是选你的答案是正确的而不是我的。 –

+1

这里没有什么特别的关于'FindClass()'的。 *除了'NewGlobalRef()'和'NewWeakGlobalRef()'外,返回引用的所有东西都返回本地引用。 – EJP