Vio*_*ffe 5 c++ java-native-interface android dalvik android-ndk
我有一个使用NDK的Android应用程序 - 一个带有常规UI和C++核心的常规Android Java应用程序.核心中有些地方我需要调用Java方法,这意味着我需要一个JNIEnv*for该线程,这反过来意味着我需要调用JavaVM->AttachCurrentThread()才能获得有效的env.
以前,只是做了AttachCurrentThread,根本没有分手.它在Dalvik中运行良好,但是一旦一个调用AttachCurrentThread退出而没有调用的线程,ART就会中止该应用程序DetachCurrentThread.所以我已经阅读了JNI参考资料,事实上它说我必须打电话DetachCurrentThread.但是当我这样做时,ART会使用以下消息中止应用程序:
试图在仍然运行代码时分离
这里有什么问题,以及如何DetachCurrentThread正确呼叫?
如果线程退出而没有分离,Dalvik也会中止.这是通过并行线程执行的关键-见threadExitCheck()在Thread.cpp.
除非调用堆栈为空,否则线程可能不会分离.这背后的原因是确保synchronized在堆栈展开时正确释放监视器锁(即语句)等任何资源.
根据规范的定义,第二次和后续的附加调用是低成本的无操作.没有引用计数,所以无论发生了多少附件,分离总是分离.一种解决方案是添加您自己的引用计数包装器.
另一种方法是每次都附加和分离.应用程序框架在某些回调中使用它.这不是一个慎重的选择,而是围绕主要用C++开发的代码包装Java源代码的副作用,并尝试在功能方面发挥作用.如果你看一下SurfaceTexture.cpp,特别是JNISurfaceTextureContext::onFrameAvailable()你可以看到SurfaceTexture需要调用Java语言回调函数,它将附加线程,调用回调,然后如果线程刚刚附加,它将立即分离它.通过调用GetEnv以查看先前是否附加了线程来设置"needsDetach"标志.
这在性能方面不是一件好事,因为每个附件需要分配一个Thread对象并执行一些内部VM内务处理,但它确实产生了正确的行为.
我将尝试一种直接而实用的方法(使用示例代码,不使用类)为偶尔在 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
}
}
Run Code Online (Sandbox Code Playgroud)
看到GetEnv的调用 API 文档后,一切都有意义了:
返回:如果当前线程未连接到 VM,则将 *env 设置为 NULL,并返回 JNI_EDETACHED。如果不支持指定的版本,则将 *env 设置为 NULL,并返回 JNI_EVERSION。否则,将 *env 设置为适当的接口,并返回 JNI_OK。
归功于: - 这个问题在调用 JavaVm->DetachCurrentThread 时出现错误“在仍在运行代码时尝试分离”,在其示例中明确指出每次都需要仔细检查(即使在调用分离之前它不会这样做)它)。- @Michael 在这个问题的评论中,他清楚地指出不调用 detach。- @fadden 所说的:“没有引用计数,所以无论发生了多少次附加,分离总是分离。”
| 归档时间: |
|
| 查看次数: |
4342 次 |
| 最近记录: |