【问题标题】:JNI Environment PointerJNI 环境指针
【发布时间】:2016-08-16 13:45:38
【问题描述】:

我有一个 Java 类,其中有一个必须从我的 C 代码调用的函数。 函数如下:

public void endTrial(){
    //Code
}

所以我在我的 C 文件中创建了以下代码:

JNIEXPORT void JNICALL package_endTrialJava();
JNIEXPORT void JNICALL package_endTrialJava(){
    jobject javaObjectRef = env->NewObject(javaClassRef, javaMethodRef);
    env->CallVoidMethod(javaObjectRef, javaMethodRef);
}

但是为了能够使用env 变量调用这个函数,我创建了这个函数,每当我启动我的程序时都会调用它。

JNIEXPORT void JNICALL package_initJNI(JNIEnv* en, jobject ob)
{
    try {
        // LOGD("(JNI) [FluidMechanics] loadVelocityDataSet()");

        if (!App::getInstance())
            throw std::runtime_error("init() was not called");

        if (App::getType() != App::APP_TYPE_FLUID)
            throw std::runtime_error("Wrong application type");


        FluidMechanics* instance = dynamic_cast<FluidMechanics*>(App::getInstance());
        android_assert(instance);
        jclass dataClass = en->FindClass("fr/limsi/ARViewer/MainActivity");
        javaClassRef = (jclass) env->NewGlobalRef(dataClass);
        javaMethodRef = env->GetMethodID(javaClassRef, "endTrial", "()V");

        env = en ;
        obj = ob ;        

    } catch (const std::exception& e) {
        throwJavaException(env, e.what());
    }
}

我的代码中也声明了这些全局变量,因此对 initJNI() 的调用可以存储 JNIEnv 变量。

JNIEnv* env ;
jobject obj ;

但是我的程序崩溃了,所以存储JNIEnv 变量似乎不起作用。那么有什么解决办法吗?

【问题讨论】:

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


    【解决方案1】:

    您不能/不应该存储JNIEnv 指针。 Is 仅对当前线程有效。
    但是您可以使用AttachCurrentThread 为您当前的线程获取JNIEnv指针。
    或者,如果您知道它已经附加,您可以使用GetEnv
    除此之外,您没有提及如何使用全局 jobject obj,但请记住,您需要注意这些引用保持足够长的有效时间。 NewGlobalRef 是要走的路。

    未经测试:

    JavaVM* g_jvm = 0;
    JNIEXPORT void JNICALL package_initJNI(JNIEnv* en, jobject ob)
    {
        ....
        // insted of the env store the VM
        en->GetJavaVM(&g_jvm);
        obj = en->NewGlobalRef(ob); // I don't think you need this
        // and at some point you must delete it again
        ....
    }
    
    JNIEXPORT void JNICALL package_endTrialJava(){
        JNIEnv* env;
        g_jvm->AttachCurrentThread(&env, NULL); // check error etc
        jobject javaObjectRef = env->NewObject(javaClassRef, javaMethodRef);
        // this line makes not much sense. I think you don't need it if you use the global
        // with the global it would be more like this
        env->CallVoidMethod(obj javaMethodRef);
    }
    

    【讨论】:

    • 感谢您的回复我已在我的 initJNI 函数中添加了代码的缺失部分,以展示我如何使用 jobject。您能否也发布代码来说明如何在我的示例中使用它?
    • @LBes:我想应该是这样的。我不完全理解你的例子。您似乎正在尝试创建一个新对象,但您也存储了一个对象。而且您的NewObjectcall 没有使用Ctor 方法参考。所以这看起来很可疑。但我希望这会有所帮助。
    • 感谢您的代码,我会尽快解决这个问题。但是,您在代码中的注释中“使用全局”是什么意思?
    • @LBes:嗯,你将一个作业对象存储在一个全局变量中。并且您在您的方法中创建一个新的并且不使用全局作业。所以我假设你不需要调用NewObject,只需使用全局作业。但这是你必须知道的。
    • 所以我尝试了这段代码(使用我在您的答案中提交的给定编辑),但我得到“运行时中止...(中止线程未附加到运行时!)转储所有线程没有适当的锁:线程列表锁突变锁”每当我调用 g_jvm->AttachCurrentThread(&env, NULL);
    猜你喜欢
    • 2020-02-11
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-09
    • 2019-01-17
    • 1970-01-01
    • 2013-09-29
    相关资源
    最近更新 更多