gpt4 book ai didi

android - 保存 JNIEnv* 的最佳方法是什么?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:39:31 65 4
gpt4 key购买 nike

我有一个使用 JNI 的 Android 项目。在实现监听器类的 CPP 文件中,有一个回调 x() 。当调用 x() 函数时,我想调用 java 类中的另一个函数。但是,为了调用该 java 函数,我需要访问 JNIEnv*。

我知道在回调的同一个cpp文件中,有一个函数:

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

调用init(..)时,我是否应该将JNIEnv*作为成员变量保存在cpp文件中?并在回调发生时使用它?

抱歉,我是 JNI 的初学者。

最佳答案

缓存 JNIEnv* 不是一个特别好的主意,因为您不能在多个线程中使用相同的 JNIEnv*,甚至可能无法使用它用于同一线程上的多个 native 调用(请参阅 http://android-developers.blogspot.se/2011/11/jni-local-reference-changes-in-ics.html)

编写一个获取JNIEnv* 并在必要时将当前线程附加到 VM 的函数并不难:

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

编辑:有时您可能有一个长时间运行的 native 线程,在多个场合需要 JNIEnv*。在这种情况下,您可能希望避免不断地将线程附加到 JVM 或从中分离线程,但您仍然需要确保在线程销毁时分离线程。

您可以通过仅附加线程一次然后保持附加,并使用 pthread_key_createpthread_setspecific 设置线程销毁回调来实现此目的调用 DetachCurrentThread

/**
* Get a JNIEnv* valid for this thread, regardless of whether
* we're on a native thread or a Java thread.
* If the calling thread is not currently attached to the JVM
* it will be attached, and then automatically detached when the
* thread is destroyed.
*/
JNIEnv *GetJniEnv() {
JNIEnv *env = nullptr;
// We still call GetEnv first to detect if the thread already
// is attached. This is done to avoid setting up a DetachCurrentThread
// call on a Java thread.

// g_vm is a global.
auto get_env_result = g_vm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (get_env_result == JNI_EDETACHED) {
if (g_vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
DeferThreadDetach(env);
} 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 env;
}

void DeferThreadDetach(JNIEnv *env) {
static pthread_key_t thread_key;

// Set up a Thread Specific Data key, and a callback that
// will be executed when a thread is destroyed.
// This is only done once, across all threads, and the value
// associated with the key for any given thread will initially
// be NULL.
static auto run_once = [] {
const auto err = pthread_key_create(&thread_key, [] (void *ts_env) {
if (ts_env) {
g_vm->DetachCurrentThread();
}
});
if (err) {
// Failed to create TSD key. Throw an exception if you want to.
}
return 0;
}();

// For the callback to actually be executed when a thread exits
// we need to associate a non-NULL value with the key on that thread.
// We can use the JNIEnv* as that value.
const auto ts_env = pthread_getspecific(thread_key);
if (!ts_env) {
if (pthread_setspecific(thread_key, env)) {
// Failed to set thread-specific value for key. Throw an exception if you want to.
}
}
}

如果 __cxa_thread_atexit 对您可用,您也许可以使用一些在其析构函数中调用 DetachCurrentThreadthread_local 对象来完成同样的事情.

关于android - 保存 JNIEnv* 的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30026030/

65 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com