【问题标题】:JNI GetMethodID cause error in native threadJNI GetMethodID 在本机线程中导致错误
【发布时间】:2014-11-05 02:26:48
【问题描述】:

在 android 中我使用 pthread_create 创建一个本地线程,然后在 Callback 过程中,调用 FindClass 来获取一个 Java 类。但它不起作用。我从android jni tips得到提示 我在FindClass from any thread in 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 %p\n", 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 %p\n", tag);
  }
}
.....
pthread_create(&handle, NULL, proc, 0);
.....

程序在 env->GetMethodID 处退出。我收到此错误:

Invalid indirect reference 0x40d8bb20 in decodeIndirectRef.

如果我从 findClass 中删除 resultClass = env-&gt;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;
}

env-&gt;FindClass(name);env-&gt;CallObjectMethod to loadClass 之间有任何冲突吗?

这是一个错误吗?有什么办法可以解决这个问题?

【问题讨论】:

  • JNIEnv* 指针是否正确获取成功?您还可以发布 getEnv() 函数的代码吗?错误是 FindClass() 还是 CallObjectMethod() 调用产生的?
  • @mario_qiu 哦,拜托。您可以亲眼看到 cmets 中的代码完全难以辨认。将其编辑到您的帖子中。
  • 代码中缺少如何以及何时获取gJvm,这也很重要。
  • 您的代码是否在findClassif (!resultClass) 分支中执行?
  • 是的。在使用 pthread_create 函数创建的本机线程中,env->FindClass 返回 null

标签: java android c++ android-ndk java-native-interface


【解决方案1】:

我使用了一些不同的代码来获取实际的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*指针,看看是否正确获取了指针?

【讨论】:

  • 登录 JNI_OnLoad 和 proc JNI_OnLoad Main Thread env is 0x2446d8 in proc env is 0x3fb150
  • 嗯,所以获取JNIEnv* 指针有效,但FindClass 仍然崩溃?您确定是FindClass 电话而不是其他电话吗?
  • jmethodID methodid = env->GetMethodID(cls, "name", "()Ljava/lang/String;");崩溃。
【解决方案2】:

不要这样做:

gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod);

尤其是,切勿获取本地引用(这是CallObjectMethod 返回的内容)并将其存储在除本地变量之外的任何内容中。

如果您想在获取本地引用的函数之外访问该值,则需要使用NewGlobalRef 获取全局引用。一旦执行返回到该线程中的 VM,本地引用就会失效。

请参阅JNI Tips 文档中的“本地和全局引用”部分。

【讨论】:

  • 感谢提示。即使我添加了 gClassLoader = env->NewGlobalRef(gClassLoader),它对我也不起作用。
【解决方案3】:

对不起,我犯了一个愚蠢的错误。

resultClass = env->FindClass(name);

env->FindClass(name) 抛出 NoClassDefFoundException 并返回 NULL。申请继续。巧合的是,jmethodID methodID = env-&gt;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 中发现了一些有用的用法 谢谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-26
    • 2016-12-15
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多