回调是否在 JNI 函数中复制 jnienv、jinstance?

Does callback copy jnienv, jinstance inside a JNI function?

我传递给构建器的 lambda 被填充到 className 对象中,并定期(每小时)调用以刷新其他成员。它第一次被成功调用。我不确定 lambda 是否保留 envinstance 以合法调用反向 JNI 函数?

JNIEXPORT jint JNICALL
Java_com_company_app_ClassName_JniInit(JNIEnv *env, jobject instance){
  int data = 0;
  auto builder = new Builder(data,
        [env, instance]() -> std::string {
            std::string stringObj = populateData(env, instance); // This function makes a reverse JNI call to get data from a java function in the class
            return stringObj;
        }
    );

  std::shared_ptr<className> = builder->build(); 

  return 1;
}

我似乎遇到了 SIGNAL 11 错误,SIGSEGV。这种分段错误是否可以通过任何方式捕获,所以应用程序不会崩溃?

Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x228 in tid 21785 (ClassName), pid 21573 (.company.app)

好像在populateData-

里面的这一行崩溃了

jstring data = (jstring)(env)->CallObjectMethod(instance, javaFunctionName);

有没有办法在调用之前检查这个函数是否会失败?我检查了 envpopulateData 中的 JNIEnv* 参数)是否为 NULL,但它不是,并且具有有效地址以及 instancejinstance populateData).

中的参数

如果此函数异步执行某些操作,您将遇到 jobject 实例的问题。原因是在这个函数启动之前,Java 将对象标记为有一个额外的引用。它会在 returns 时删除它。所以在它 returns 之后,如果 Java 代码中没有其他实例,则该对象可以被垃圾收集器清理。

这可以通过在主线程上启动异步函数之前调用 NewGlobalRef(JNIEnv *env, jobject obj) 并在不再需要 jobject 时在回调结束时调用 DeleteGlobalRef 来解决。

为了回答这个问题,我发现了一种稍微不同的技巧。

不要复制 JNIEnvobject 或创建对它们的引用。一旦您的 JNI 函数超出范围,它们就会被删除。我不确定为什么复制不起作用(如果有人能回答这个问题,那就太好了)。或者,我使用 JavaVM* 来维护 jvm 的本地引用,您可以在 JNI_OnLoad.

中执行此操作

使用下面的函数,它会工作正常。

JavaVM* jvm; // Initialise this in OnLoad

JNIEnv *getJNIEnv()
   {
        JNIEnv *env = nullptr;
        jint ret = jvm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
        if (ret == JNI_EDETACHED || !env)
        {
            env = nullptr;
        }
        return env;
   }

如果您需要 class 实例,则需要通过 JNIEnv 对其进行初始化,但我对 class 使用静态方法,所以我只获取了 class 使用 env->FindClass 然后你可以做 env->GetStaticMethodID 连同 env->CallStaticObjectMethod.