我和JNI有一个Android项目。在实现侦听器类的CPP文件中,有一个回调x()。调用x()函数时,我想调用java类中的另一个函数。然而,为了调用该java函数,我需要访问JNIEnv*。
我知道在回调的同一个cpp文件中,有一个函数:
static jboolean init (JNIEnv* env, jobject obj) {...}
当init(..)时,我应该将JNIEnv*作为成员变量保存在cpp文件中吗
被调用了吗?然后在回调发生时使用它?
抱歉,我是JNI的初学者。
@Michael很好地概述了如何通过缓存JVM来检索JNI。对于那些不想使用pthread(或者因为你在Windows系统上而不能使用pthread),并且你正在使用c 11或更高版本的系统,那么线程本地存储就是一种选择。
下面是一个关于如何实现一个包装器方法的粗略示例,该方法可以正确地连接到线程,并在线程退出时自动清理
JNIEnv* JNIThreadHelper::GetJniEnv() {
// This method might have been called from a different thread than the one that created
// this handler. Check to make sure that the JNI is attached and if not attach it to the
// new thread.
// double check it's all ok
int nEnvStat = m_pJvm->GetEnv(reinterpret_cast<void**>(&m_pJniEnv), JNI_VERSION_1_6);
if (nEnvStat == JNI_EDETACHED) {
std::cout << "GetEnv: not attached. Attempting to attach" << std::endl;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // choose your JNI version
args.name = NULL; // you might want to give the java thread a name
args.group = NULL; // you might want to assign the java thread to a ThreadGroup
if (m_pJvm->AttachCurrentThread(&m_pJniEnv, &args) != 0) {
std::cout << "Failed to attach" << std::endl;
return nullptr;
}
thread_local struct DetachJniOnExit {
~DetachJniOnExit() {
m_pJvm->DetachCurrentThread();
}
};
m_bIsAttachedOnAThread = true;
}
else if (nEnvStat == JNI_OK) {
//
}
else if (nEnvStat == JNI_EVERSION) {
std::cout << "GetEnv: version not supported" << std::endl;
return nullptr;
}
return m_pJniEnv;
}
缓存一个JNIEnv*
并不是一个特别好的主意,因为您不能跨多个线程使用同一个JNIEnv*
,甚至可能无法在同一线程上使用它进行多个本机调用(请参阅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
编辑:有时您可能有一个运行时间较长的本地线程,在许多情况下需要一个JNIEnv*
。在这种情况下,您可能希望避免不断地将线程连接到/从JVM分离,但是您仍然需要确保在线程销毁时分离线程。
您可以通过只附加一次线程,然后将其保持在附加状态,并使用pthread_key_create
和pthread_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.
}
}
}
如果\uuucxa\u thread\u atexit
对您可用,您可能可以使用一些thread\u local
对象来完成同样的事情,该对象在其析构函数中调用DetachCurrentThread
。
我在android应用程序中使用Stripe。目前,客户机服务器用于创建客户和对卡收费。 现在我想在应用程序中保存条带返回的卡信息,并在自定义ui中显示支付方式中添加的卡的列表,以便更新、删除和选择默认卡。 我浏览了stripe的临时密钥文档,他们提供了显示卡细节的活动。但是我找不到如何在自定义ui中显示卡片列表。 有没有其他方法可以实现这一点,例如在本地保存卡信息? 条纹是否允许自定义卡片列表等
我想要一份有团队成员的团队名单 (我正在用java编写示例代码,但解决方案不必使用java。) 像这样: 如果我想访问一个团队的所有成员,那将是非常直接的<但是如果我想看看一个成员是哪个团队的成员呢<我不想每次都对所有团队成员进行循环。 我可以在成员的班级里拯救每个成员的团队,但这不是有点愚蠢吗?
我需要根据一些配置数据启动多个独立的周期性任务——总数事先不知道。例如,我想检查具有不同间隔和不同参数的不同目录的内容,其中列表和参数是从配置中读取的。 在我看来,夸克斯调度器只能调度固定的、预先已知的方法。动态/编程调度的最佳方法是什么?<代码>vertx.set周期 是正确的方法还是我应该以某种方式访问Quartz?
问题内容: 父类中有一个列表List。保存父项后,已添加或更改的子项应通过hibernate保存/更新。 我找到了很多解释,但是,我只是没有使它起作用。 Parent.class试试A Parent.class试试B 孩子被添加到新的父母。之后两者都保存 但是在冲洗时,出现以下错误: 有人看到我的错误吗? 非常感谢!! 问题答案: 我想如果您从第一条评论中回答这个问题,我们就会遇到这种情况: 你有
问题内容: 什么是最好的Java图像处理库/方法?[关闭]我同时使用JAI媒体API和ImageMagick吗? ImageMagick存在一些可伸缩性问题,基于JNI的JMagick也没有吸引力。与ImageMagick相比,JAI执行大小调整操作时的质量结果较差。 有谁知道过任何出色的开源或商业工具,它们都是本机Java并可以提供高质量的结果? 问题答案: 我知道这个问题已经很老了,但是随着新
问题内容: 我想使用php / pdo制作一个后端应用程序。我发现了很多不同的方法来进行PDO连接字符串。我想知道,使用pdo做连接字符串的最佳方法是什么。这是做连接字符串的最佳方法还是我应该使用其他一些代码。欢迎任何建议或调整! 这是我目前所拥有的: 问题答案: 这个问题并不像看起来那么容易。我写了一个规范的示例, 如何使用PDO连接到MySQL 。 因此,让我们看一下可以改进的地方: 可以将c