11

Here I create a class in JAVA in which I have function (callback) which I must call from C file.

class DSMInitializeClassParameter {

    /**
     * Callback function for DSM Initialize.
     */
    public void DSMInitializeCallback( ) {

        // Write Message To Logs.
        System.out.println( "Dsm Initialize Callback called." );
    }
}

For that I have created native method which must be called.

public class DsmLibraryTest extends Activity {
     // Some code ....

     // Create a DSMInitializeClassParameter  class object.
     DSMInitializeClassParameter object = new DSMInitializeClassParameter();
     // Call native method with given object.
     nativeMethod( object );

     // Some code ....

     // Implementation of native method.
     public native int nativeMethod(DSMInitializeClassParameter classObject);
}

In C file I have following:

dsmResult_t dsmInitializeCall( dsmResult_t status, void * pUserData, dsmEvent_t * hEvent ) {

    (*env)->CallVoidMethod(env, classObject, mid);
}

JNIEXPORT jint JNICALL Java_com_Dsm_Test_DsmLibraryTest_nativeMethod(JNIEnv* env, jobject obj, jobject classObject) {
    // This function loads a locally-defined class. It searches the directories and zip
    // files specified by the CLASSPATH environment variable for the class with the specified name.
    jclass cls = (*env)->FindClass( env, "com/Dsm/Test/DSMInitializeClassParameter" );
    // Get java Method.
    jmethodID mid = (*env)->GetMethodID(env, cls, "DSMInitializeCallback", "()V");
    // If no method was found return -1;
    if( mid == NULL ) {
        return -1;
    }

    // Call DSM Initialize Callback Function and return value.
    return dsmInitialize( dsmInitializeCall, NULL );
}

how you can see I want to call (*env)->CallVoidMethod(env, classObject, mid); from dsmInitializeCall function, but how I can call it if I have no env, classObject and mid I try with statics but it not work.

Viktor Apoyan
  • 10,655
  • 22
  • 85
  • 147

2 Answers2

26

It's generally unsafe to cache a JNIEnv* instance and keep using it, as it varies depending on the currently active thread. You can save a JavaVM* instance, which will never change. In a native initializer function, call GetJavaVM and pass it the address of a JavaVM pointer:

static JavaVM *jvm;
JNIEXPORT void JNICALL Java_SomeClass_init(JNIEnv *env, jclass) {
    int status = (*env)->GetJavaVM(env, &jvm);
    if(status != 0) {
        // Fail!
    }
}

Now you can use that JavaVM* to get the current JNIEnv* with AttachCurrentThread:

dsmResult_t dsmInitializeCall( dsmResult_t status, void * pUserData, dsmEvent_t * hEvent ) {
    JNIEnv *env;
    (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
    (*env)->CallVoidMethod(env, classObject, mid);
}
Michael Mrozek
  • 169,610
  • 28
  • 168
  • 175
  • Thanks a lot, I will check tomorrow at my workplace ... And may be you can help me in this question ? http://stackoverflow.com/questions/6152747/jni-not-support-types-as-void-unsigned-int-what-to-do – Viktor Apoyan Jun 01 '11 at 20:04
  • And why I cant use jclass cls = (*env)->GetObjectClass( env, classObject ); Instead of jclass cls = (*env)->FindClass( env, "com/Dsm/Test/DSMInitializeClassParameter" ); I think that classObject is object to my class in which I have DSMInitializeCallback so I can get class from that object. But when I try application crashed with Dalvic VM Error .... But why ? – Viktor Apoyan Jun 02 '11 at 05:35
  • http://stackoverflow.com/questions/6184678/where-can-i-see-message-from-jni-printf-in-eclipse – Viktor Apoyan Jun 02 '11 at 08:35
  • 3
    @hubatish Your [suggested edit](http://stackoverflow.com/review/suggested-edits/14528119) is for C++ code, this question is in C – Michael Mrozek Dec 07 '16 at 20:08
9

Another way to ensure that you get a reference to the JavaVM as the first order of business is to add the JNI_OnLoad method and cache the reference. This will be called when the shared library gets loaded.

Ex.

static JavaVM* cachedJVM;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
{
    cachedJVM = jvm;
    // ... Any other initialization code.
}

Once you have the ref to the JavaVM pointer you can then use the method Michael Mrozek described in the above post.

Community
  • 1
  • 1