What is the best way to keep JNIEnv *

I have an Android project with JNI. There is an x ​​() callback in the CPP file that implements the listener class. When the x () function is called, I want to call another function in the java class. However, in order to call this java function, I need to access the JNIEnv *.

I know there is a function in the same callback cpp file:

static jboolean init (JNIEnv* env, jobject obj) {...}

      

Should I save JNIEnv * as a member variable in the cpp file when called init(..)

? and use it later when the callback occurs?

Sorry, but I'm new to JNI.

+3


source to share


1 answer


Caching a is JNIEnv*

not a particularly good idea as you cannot use the same JNIEnv*

for multiple threads and may not even be able to use it for multiple native calls on the same thread (see http://android-developers.blogspot.se /2011/11/jni-local-reference-changes-in-ics.html )

Writing a function that receives JNIEnv*

and optionally attaches the current thread to the VM isn't too difficult:

bool GetJniEnv(JavaVM *vm, JNIEnv **env) {
    bool did_attach_thread = false;
    *env = nullptr;
    // Check if the current thread is attached to the VM
    auto get_env_result = vm->GetEnv((void**)env, JNI_VERSION_1_6);
    if (get_env_result == JNI_EDETACHED) {
        if (vm->AttachCurrentThread(env, NULL) == JNI_OK) {
            did_attach_thread = true;
        } else {
            // Failed to attach thread. Throw an exception if you want to.
        }
    } else if (get_env_result == JNI_EVERSION) {
        // Unsupported JNI version. Throw an exception if you want to.
    }
    return did_attach_thread;
}

      

How to use:



JNIEnv *env;
bool did_attach = GetJniEnv(vm, &env);
// Use env...
// ...
if (did_attach) {
   vm->DetachCurrentThread();
}

      

You can wrap this in a class that attaches to the construct and detaches from destructive, RAII-style:

class ScopedEnv {
public:
    ScopedEnv() : attached_to_vm_(false) {
        attached_to_vm_ = GetJniEnv(g_vm, &env_);  // g_vm is a global
    }

    ScopedEnv(const ScopedEnv&) = delete;
    ScopedEnv& operator=(const ScopedEnv&) = delete;

    virtual ~ScopedEnv() {
        if (attached_to_vm_) {
            g_vm->DetachCurrentThread();
            attached_to_vm_ = false;
        }
    }

    JNIEnv *GetEnv() const { return env_; }

private:
    bool attached_to_env_;
    JNIEnv *env_;
};

// Usage:

{
    ScopedEnv scoped_env;
    scoped_env.GetEnv()->SomeJniFunction();
}
// scoped_env falls out of scope, the thread is automatically detached if necessary

      

+11


source







All Articles