39

I am storing off JNIEnv in a global so I can call static java methods later. But is it nessasary to store off a global pointer to the JNIEnv, they way one would with any other java object, or is it a special case that does not require this.

JNIEnv* globalEnvPointer;

[JNICALL etc] void init(JNIENv* env, [etc])
{
   //required?
   globalEnvPointer = (JNIENv*) (env*)->GetGlobalRef(env, env);
   //or is this OK?
   globalEnvPointer = env;
}

Edit

I'm bing a bit dumb here, all the methods that will use globalEnvPointer, are invoked within my init because my init is actually my c program's main method, which won't return until the end of the program. I am also using no other threads in the c program. I think this simplifies the answer.

JNIEnv* globalEnvPointer;

[JNICALL etc] void main(JNIENv* env, [etc])
{
   //required?
   globalEnvPointer = (JNIENv*) (env*)->GetGlobalRef(env, env);
   //or is this OK?
   globalEnvPointer = env;
   someMethod();
}

void someMethod()
{
   //use globalEnvPointer here
}
weston
  • 54,145
  • 21
  • 145
  • 203

1 Answers1

56

You cannot cache the JNIEnv pointer. Read about it here:

The JNI interface pointer (JNIEnv) is valid only in the current thread. Should another thread need to access the Java VM, it must first call AttachCurrentThread() to attach itself to the VM and obtain a JNI interface pointer. Once attached to the VM, a native thread works just like an ordinary Java thread running inside a native method. The native thread remains attached to the VM until it calls DetachCurrentThread() to detach itself.

What you can do is to cache the JavaVM pointer instead.

static JavaVM *jvm;

[JNICALL etc] void init(JNIENv* env, [etc])
{
   jint rs = (*env)->GetJavaVM(env, &jvm);
   assert (rs == JNI_OK);
}

And then whenever you need then JNIEnv pointer from a context where it is not given you do this:

void someCallback() {
    JNIEnv *env;
    jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
    assert (rs == JNI_OK);
    // Use the env pointer...
}

But whenever you call a native method from Java the env pointer to use is given:

JNIEXPORT jint JNICALL Java_package_Class_method(JNIEnv *env, jobject obj) {
    // just use the env pointer as is.
}
maba
  • 47,113
  • 10
  • 108
  • 118
  • It's all one thread, does that make a difference? i.e. my `init` is called in the same thread as that will later call the static java methods. – weston Sep 14 '12 at 09:27
  • Always use the `JNIEnv` pointer that is coming in in the C function. As in my last example. – maba Sep 14 '12 at 09:28
  • Please see my edit, in your example, if `Java_package_Class_method` called `someCallback`, you wouldn't need to go via `JavaVM` would you? In effect this is what I am doing, just using a global rather than passing it to `someCallback`. – weston Sep 14 '12 at 09:39
  • 1
    You could but isn't that kind of ugly? Storing temporary data in global variables to be used from other methods is an anti-pattern in my eyes. – maba Sep 14 '12 at 09:49
  • 1
    Yes I do agree, but I am trying to take a huge `c` source base and rewrite 100+ assembly methods in java (like `someCallback`). We're talking a few thousand places were invokations are performed. Plus the same source base should still compile for it's original target, so passing `JNIEnv` around is actually the ugly solution. Thanks for the all the tips, I will bare them in mind if I need to use threads. – weston Sep 14 '12 at 10:25
  • 1
    I wouldn't create my own init function if there is already JNI_OnLoad, which passes the JavaVM. – DanielB May 17 '17 at 12:58
  • @DanielB That's something you can tell the OP in a comment under his question. I merely used his own functions. He may have some reason that we don't know about to use an init function. – maba Aug 31 '17 at 13:20
  • 4
    @maba: The OP might need a separate init function, but maybe he also doesn't know about the existence of JNI_OnLoad at all. It just extends the answer. It is not meant as critic, just as a friendly little extension hint. I don't write my own answer, because I think your answer is already good and you should get the points. However, also think about other people reading your answer, not just the OP and there are experienced and unexperienced people under them. – DanielB Sep 01 '17 at 17:16