我有一个使用NDK的Android应用程序 - 一个具有常规UI和C ++核心的常规Android Java应用程序。我需要在核心中调用Java方法,这意味着我需要JNIEnv*
该线程,这反过来意味着我需要调用JavaVM->AttachCurrentThread()
来获取有效的env
}。
以前,只是做AttachCurrentThread
并且根本没有分离。它在Dalvik中运行良好,但只要调用AttachCurrentThread
的线程退出而不调用DetachCurrentThread
,ART就会中止应用程序。所以我已经阅读了JNI参考资料,事实上它说我必须致电DetachCurrentThread
。但是,当我这样做时,ART会使用以下消息中止该应用程序:
尝试在仍然运行代码时分离
这里的问题是什么,以及如何恰当地致电DetachCurrentThread
?
答案 0 :(得分:8)
threadExitCheck()
。
除非调用堆栈为空,否则线程可能不会分离。这背后的原因是确保在堆栈展开时正确释放监视器锁(即synchronized
语句)等任何资源。
根据规范的定义,第二次和后续的附加调用是低成本的无操作。没有引用计数,所以无论发生了多少附件,分离总是分离。一种解决方案是添加自己的引用计数包装器。
另一种方法是每次都附加和分离。应用程序框架在某些回调中使用它。这不是一个有意识的选择,而是围绕主要用C ++开发的代码包装Java源代码的副作用,并尝试在功能方面崭露头角。如果你看一下SurfaceTexture.cpp,特别是{{ 1}},你可以看到当SurfaceTexture需要调用Java语言回调函数时,它将附加线程,调用回调,然后如果刚刚附加了线程,它将立即将其分离。 "需要Detach"通过调用JNISurfaceTextureContext::onFrameAvailable()
来设置标志,以查看该线程之前是否已附加。
这在性能方面并不是一件好事,因为每个附件都需要分配一个Thread对象并执行一些内部VM内务处理,但它确实会产生正确的行为。
答案 1 :(得分:3)
我将尝试一种直接且实用的方法(使用示例代码,而不使用类)为偶尔在android中出现此错误的开发人员解答此问题,以防他们在正常工作的情况下以及在操作系统或框架更新(Qt?),开始出现有关该错误和消息的问题。
JNIEXPORT void Java_com_package_class_function(JNIEnv* env.... {
JavaVM* jvm;
env->GetJavaVM(&jvm);
JNIEnv* myNewEnv; // as the code to run might be in a different thread (connections to signals for example) we will have a 'new one'
JavaVMAttachArgs jvmArgs;
jvmArgs.version = JNI_VERSION_1_6;
int attachedHere = 0; // know if detaching at the end is necessary
jint res = jvm->GetEnv((void**)&myNewEnv, JNI_VERSION_1_6); // checks if current env needs attaching or it is already attached
if (JNI_EDETACHED == res) {
// Supported but not attached yet, needs to call AttachCurrentThread
res = jvm->AttachCurrentThread(reinterpret_cast<JNIEnv **>(&myNewEnv), &jvmArgs);
if (JNI_OK == res) {
attachedHere = 1;
} else {
// Failed to attach, cancel
return;
}
} else if (JNI_OK == res) {
// Current thread already attached, do not attach 'again' (just to save the attachedHere flag)
// We make sure to keep attachedHere = 0
} else {
// JNI_EVERSION, specified version is not supported cancel this..
return;
}
// Execute code using myNewEnv
// ...
if (attachedHere) { // Key check
jvm->DetachCurrentThread(); // Done only when attachment was done here
}
}
看到GetEnv的The Invocation API docs之后,一切都变得有意义了:
退货: 如果当前线程未连接到VM,则将* env设置为NULL,并返回JNI_EDETACHED。如果不支持指定的版本,请将* env设置为NULL,并返回JNI_EVERSION。否则,将* env设置为适当的接口,并返回JNI_OK。
信用至: -这个问题Getting error "attempting to detach while still running code" when calling JavaVm->DetachCurrentThread在其示例中明确表明,有必要每次都进行仔细检查(即使在调用detach之前并没有这样做)。 -@Michael在此问题中的评论他清楚地指出了有关不调用detach的内容。 -@fadden说的是:“没有引用计数,因此无论发生了多少附件,分离总是分离。”