2015-05-04 4 views
3

У меня есть проект Android с JNI. В CPP-файле, который реализует класс слушателя, есть обратный вызов x(). Когда вызывается функция x(), я хочу вызвать другую функцию в классе java. Однако, чтобы вызвать эту функцию java, мне нужно получить доступ к JNIEnv *.Каков наилучший способ сэкономить JNIEnv *

Я знаю, что в том же CPP файл обратного вызова, есть функция:

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

Если я сохранить в CPP файле JNIEnv * в качестве переменной-члена, когда init(..) называется? и использовать его позже, когда происходит обратный вызов?

Извините, но я новичок в JNI.

ответ

11

Кэширование JNIEnv* не особенно хорошая идея, так как вы не можете использовать тот же JNIEnv* через несколько потоков, и может даже не быть в состоянии использовать его для нескольких нативных вызовов в том же потоке (см http://android-developers.blogspot.se/2011/11/jni-local-reference-changes-in-ics.html)

Дать функцию которая получает JNIEnv* и крепит текущий поток к ВМ, если необходимо, это не так уж сложно:

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

, как вы бы использовать это:

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

Вы можете обернуть это в классе, который прикрепляет на строительство и отсоединяется от разрушения, RAII-стиле:

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