JNI GetMethodID导致本机线程出错
JNI GetMethodID cause error in native thread
在android中,我使用pthread_create创建一个本地线程,然后在回调过程中,调用FindClass来获得一个Java类。但它不起作用。我从android jni tips得到提示我在FindClass中从Android JNI 中的任何线程找到了解决方案
我这样修改我的项目[编辑]
JavaVM* gJvm = nullptr;
static jobject gClassLoader;
static jmethodID gFindClassMethod;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
gJvm = pjvm; // cache the JavaVM pointer
auto env = getEnv();
//replace with one of your classes in the line below
auto randomClass = env->FindClass("com/example/RandomClass");
jclass classClass = env->GetObjectClass(randomClass);
auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
"()Ljava/lang/ClassLoader;");
gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod);
gClassLoader = env->NewGlobalRef(gClassLoader);
gFindClassMethod = env->GetMethodID(classLoaderClass, "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
//check. this is ok
jclass cls = env->FindClass("com/example/data/DataTest");
jmethodID methoID = env->GetMethodID(cls, "name", "()Ljava/lang/String;");
LOG_INFO("cls is %pn", cls);
return JNI_VERSION_1_6;
}
JNIEnv* getEnv() {
JNIEnv *env;
int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);
if(status < 0) {
status = gJvm->AttachCurrentThread(&env, NULL);
if(status < 0) {
return nullptr;
}
}
return env;
}
jclass findClass(const char* name) {
JNIEnv *env = getEnv();
jclass resultClass = 0;
if(env)
{
resultClass = env->FindClass(name);
//it can not found class in native thread, use loadClass method
if (!resultClass)
{
LOG_INFO("can not find the class");
//return value is not null.
return static_cast<jclass>(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name)));
}
}
return resultClass;
}
.......
//thread callback
void *proc(void *)
{
JNIEnv *env = getEnv();
jclass cls = findClass("com/example/data/DataTest");
if (cls)
{
LOG_INFO("GetMethodID");
//crash
jmethodID methodID = env->GetMethodID(cls, "name", "()Ljava/lang/String;");
LOG_INFO("proc tag is %pn", tag);
}
}
.....
pthread_create(&handle, NULL, proc, 0);
.....
程序在env->GetMethodID处退出。我得到这个错误:
Invalid indirect reference 0x40d8bb20 in decodeIndirectRef.
如果我从findClass中删除resultClass = env->FindClass(name);
,那没关系。可以打印"proc-tag is"。
//correct
jclass findClass(const char* name) {
JNIEnv *env = getEnv();
jclass resultClass = 0;
if(env)
{
if (!resultClass)
{
return static_cast<jclass>(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name)));
}
}
return resultClass;
}
CCD_ 2和CCD_?
是虫子吗?可以做些什么来解决这个问题?
不要这样做:
gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod);
特别是,永远不要取一个局部引用(CallObjectMethod
返回的是这个引用)并将其存储在除局部变量之外的任何内容中。
如果要在获取本地引用的函数之外访问该值,则需要使用NewGlobalRef
获取全局引用。一旦执行返回到该线程中的VM,本地引用就会失效。
请参阅JNI Tips文档中的"本地和全局引用"部分。
对不起,我犯了一个愚蠢的错误。
resultClass = env->FindClass(name);
env->FindClass(name)抛出NoClassDefFoundException并返回NULL。应用程序继续运行。jmethodID methodID = env->GetMethodID(cls, "name", "()Ljava/lang/String;");
中的VM意外中止。可能有一种解决方案是:
jclass findClass(const char* name) {
JNIEnv *env = getEnv();
jclass resultClass = 0;
if(env)
{
resultClass = env->FindClass(name);
jthrowable mException = env->ExceptionOccurred();
if (mException )
{
env->ExceptionDescribe();
env->ExceptionClear();
return static_cast<jclass>(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name)));
}
}
return resultClass;
}
我在中发现了一些有用的用法http://android.wooyd.org/JNIExample谢谢
我使用了一个稍微不同的代码来获得实际的JNIEnv*
指针(它基于SO某处发布的代码示例)。这种方法被证明是可靠的。
class BaseJNI
{
protected:
BaseJNI(JNIEnv * env)
{
int ret = env->GetJavaVM(&jvm);
if( ret != JNI_OK )
{
LOG_INFO("Could not get JavaVM: %d", ret);
throw;
}
}
JNIEnv * GetEnv()
{
JNIEnv * env;
// double check it's all ok
int ret = jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
if (ret == JNI_OK)
return env;
if (ret == JNI_EDETACHED)
{
LOG_INFO("GetEnv: thread is not attached, trying to attach");
ret = jvm->AttachCurrentThread(&env, nullptr);
if( ret == JNI_OK )
{
LOG_INFO("GetEnv: attach successful");
return env;
}
LOG_INFO("Cannot attach JNI to current thread: %d", ret);
return nullptr;
}
LOG_INFO("could not get JNI ENV: %d", ret);
return nullptr;
}
protected:
JavaVM *jvm;
};
当您确实有一个有效的JNIEnv*
指针时,这个类应该在主线程中实例化,然后它也可以从后台线程中使用。
你能用上面的代码获取JNIEnv*
指针吗?看看指针是否正确获取?
- 使用 thread 类在 C++ 中构造线程的动态数组时出错
- C++ 线程时出错,标准::调用:
- 混合开放MP/MPI代码中的多线程发送/接收时出错
- 将工作线程信号与主窗口插槽 (Qt5) 连接时出错
- std::c++ 中的线程在设置线程函数时不会出错
- std::move在将std::string移动到另一个线程时出错
- 编译类 C++ 中的线程时出错(错误 xthread)
- 在C++中创建线程时出错
- 在另一个线程正在运行的情况下退出应用程序时出错
- 在 Ubuntu 13.10 x64 C++11 中创建 std::线程时出错
- 提升时出错"member function redeclaration not allowed":线程
- 多个线程无法访问同一个指针而不会出错
- MFC 编程:编译时出错:线程代码中的错误
- 线程不刷新数据,在屏幕中显示数据时出错
- 线程调用类函数时出错
- JNI GetMethodID导致本机线程出错
- 读取线程应用程序(linux,pthreads)中的文件大小时出错
- 使用类函数创建线程时出错
- 在c++中创建简单线程时出错
- 在代码块中编译线程程序时出错