当前位置: 首页 > 知识库问答 >
问题:

JNI GetMethodID导致本机线程出错

易烨磊
2023-03-14

在android中,我使用pthread_create来创建一个本地线程,然后在回调过程中,调用FindClass来获取一个Java类。但是它不起作用。我从android jni提示中获得提示,我从Android JNI中的任何线程中找到了FindClass中的解决方案

我为我的项目修改它,如下[编辑]

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退出-

Invalid indirect reference 0x40d8bb20 in decodeIndirectRef.

如果我删除resultClass=env-

//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;
}

环境之间的任何冲突-

是虫子吗?可以做些什么来解决这个问题?


共有3个答案

邢博文
2023-03-14

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

resultClass = env->FindClass(name);

环境-

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谢谢

郎增
2023-03-14
匿名用户

我使用了一些不同的代码来获取实际的JNIEnv*指针(它基于发布在这里某处的代码示例)。事实证明,这种方法工作可靠。

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*指针,看看指针获取是否正确吗?

季华茂
2023-03-14

不要这样做:

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

特别是,永远不要获取本地引用(这是CallObjectmethod返回的内容)并将其存储在局部变量以外的任何内容中。

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

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

 类似资料:
  • 问题内容: TL; DR: 将任何非内置函数添加到Array.prototype AND Function.prototype将导致IE8本机JSON解析器在解析包含数组的任何JSON时发生堆栈溢出,但仅当您还传递了reviver函数时放入JSON.parse()。 最初这是一个问题,但我回答了我自己的原始问题,所以现在我要问:有人能想到此IE8错误的解决方法,该方法不涉及消除所有修改Array.

  • 更像是这个问题:“java.lang.OutOfMemoryError:无法创建新的本机线程” 原因:java。lang.OutOfMemoryError:无法在java上创建新的本机线程。朗。丝线。start0(本机方法),位于java。朗。丝线。从org开始(Thread.java:717)。日食码头。util。线QueuedThreadPool。startThread(QueuedThrea

  • 我正在尝试在我的ionic应用程序中实现推送通知。为此,我想使用本机插件Push: https://ionicframework.com/docs/native/push/ 在安装这个插件之前,我可以在我的Android设备上启动我的应用程序,使用: 我不能再运行我的设备上的应用程序,因为构建失败,因为一堆错误。 这个错误有一个建议:将'tools:replace=“android:value”‘

  • 问题内容: 我已经使用线程编写了python tkinter代码,以便tkinter向导通过在主线程中运行的tkinter mainloop和在单独线程中运行的后台进程自动更新。但是我注意到,运行代码一段时间后python崩溃了。此外,它本质上是随机的,但python大部分时间都崩溃。我写了一个小的测试代码来显示这个问题(我的原始代码与此类似,但是具有一些实际的过程和许多其他功能,因此我将共享测试

  • 我有一个Android应用程序,它由一些本机线程(未连接到JVM)组成,需要能够调用Java对象的方法。 我打算这样做的方式是创建一个JNI函数,我从相关的Java对象调用它,它允许我在静态本机数据结构中获取和缓存所需的java对象方法ID、JNIEnv和对象引用,以便我的本机线程可以(线程安全地)访问所需的方法(例如,使用(*env)- 我不相信这种方法会起作用,因为我读到JNIEnv指针不能在

  • 问题内容: 绿色线程和本机线程有什么区别? 为什么将其命名为绿色和原生? 我是编程世界的新手。我喜欢学习Java。在经历Java线程面试问题时,我发现了这一点。我听说过线程,但是没有听说过这些绿色线程和本地线程。我对绿色线程和本机线程感到困惑,但不清楚。 在这种情况下,线程被称为绿色线程还是本地线程?(我的意思是在编程中) 问题答案: 绿色线程和本机线程有什么区别? 绿色线程由虚拟机调度。 本机线