2017-09-20 6 views
1

J'essaie d'utiliser JNI dans android pour faire un pointeur de fonction qu'une bibliothèque native que j'utilise utilise son appel à java.Appel de la méthode Java statique à partir d'un thread séparé en utilisant JNI

Lorsque initializeStateController est appelée, un nouveau thread est créé en utilisant pthread_create qui appelle le pointeur de fonction à chaque fois que l'état du contrôleur d'état change.

Cependant, quand je tente d'appeler GetStaticMethodID de state_exec en C, je reçois l'erreur suivante:

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

Ceci est mon code actuel:

C Code de

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

Répondre

1

Il s'avère que la méthode FindClass renvoie uniquement des références locales, pas globales. Pour faire ce travail, vous devez en faire une référence globale. Vous pouvez le faire comme démontré ci-dessous.

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

d'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

+0

Je posté ma réponse juste avant le vôtre comme je l'avais compris moi-même, mais je comprends la réponse et je vais marquer votre réponse comme correcte au lieu du mien. –

+1

Il n'y a rien de spécial à propos de 'FindClass()' ici. * Tout * qui renvoie des références renvoie des références locales, sauf pour 'NewGlobalRef()' et NewWeakGlobalRef() '. – EJP