You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多次调用CallVoidMethod致React Native JSI-JNI崩溃求助

问题分析与修复方案

核心崩溃原因

  1. JNIEnv跨线程非法访问:你在nativeInstall中捕获的JNIEnv*属于调用该方法的线程(通常是主线程),但JSI回调是在JS引擎线程(mqt_v_js)执行的。JNIEnv是线程绑定的,跨线程使用会直接触发内存访问错误(SIGSEGV)。
  2. 未缓存全局引用的jclass:每次回调都调用FindClass,不仅效率低下,还可能因局部引用积累引发隐性内存问题。
  3. JSI函数参数声明错误:原代码中createFromHostFunction的参数个数写为0,但实际需要接收1个参数,会导致参数校验逻辑失效。

修复步骤

1. 保存JavaVM指针而非JNIEnv

JNI允许通过JavaVM在任意线程获取当前线程的JNIEnv,修改nativeInstall方法:

extern "C"
JNIEXPORT void JNICALL
Java_com_mymodule_rtnstreamstream_RTNStreamModule_nativeInstall(JNIEnv *env, jobject thiz, jlong jsi) {
    jsi::Runtime * runtime = reinterpret_cast<jsi::Runtime *>(jsi);
    // 获取全局有效的JavaVM指针
    JavaVM* vm = nullptr;
    env->GetJavaVM(&vm);
    // 创建模块实例的全局引用
    jobject rtnstream_kt_module = env->NewGlobalRef(thiz);
    // 传递JavaVM而非JNIEnv到install方法
    rtnstream::install(*runtime, vm, rtnstream_kt_module);
}

2. 缓存jclass全局引用

在install函数中提前获取并缓存模块类的全局引用,避免每次回调重复查找:

// 全局变量,用于缓存模块类的全局引用
static jclass s_rtn_stream_class = nullptr;

void install(jsi::Runtime& runtime, JavaVM* vm, jobject module_ref) {
    JNIEnv* env = nullptr;
    vm->AttachCurrentThread(&env, nullptr);
    
    // 将局部类引用转为全局引用缓存
    jclass local_class = env->FindClass("com/myModule/RTNStreamModule");
    s_rtn_stream_class = reinterpret_cast<jclass>(env->NewGlobalRef(local_class));
    env->DeleteLocalRef(local_class);
    
    // 创建JSI函数,修正参数个数为1
    jsi::Function receiveDataFunc = jsi::Function::createFromHostFunction(
        runtime,
        jsi::PropNameID::forAscii(runtime, "receiveDataFromCpp"),
        1,
        [vm, module_ref](
                jsi::Runtime& runtime,
                const jsi::Value& thisValue,
                const jsi::Value* arguments,
                std::size_t count
        ) -> jsi::Value {
            if (count != 1 || !arguments[0].isObject()) {
                throw jsi::JSError(runtime, "Argument has to be an ArrayBuffer");
            }

            jsi::Object object = arguments[0].asObject(runtime);
            jsi::ArrayBuffer arrayBuffer = object.getArrayBuffer(runtime);

            size_t bufferSize = arrayBuffer.size(runtime);
            uint8_t* bufferData = arrayBuffer.data(runtime);

            // 获取当前线程的JNIEnv
            JNIEnv* currentEnv = nullptr;
            jint result = vm->AttachCurrentThread(&currentEnv, nullptr);
            if (result != JNI_OK) {
                throw jsi::JSError(runtime, "Failed to attach JNI thread");
            }

            jbyteArray jbyteArrayData = currentEnv->NewByteArray(bufferSize);
            if (jbyteArrayData == nullptr) {
                throw jsi::JSError(runtime, "Failed to create byte array");
            }
            currentEnv->SetByteArrayRegion(jbyteArrayData, 0, bufferSize, reinterpret_cast<const jbyte*>(bufferData));

            // 使用缓存的全局类引用获取方法ID
            jmethodID receiveDataMethod = currentEnv->GetMethodID(s_rtn_stream_class, "receiveDataFromCpp", "([B)V");
            if (receiveDataMethod != nullptr) {
                currentEnv->CallVoidMethod(module_ref, receiveDataMethod, jbyteArrayData);
            } else {
                throw jsi::JSError(runtime, "Failed to find receiveDataFromCpp method");
            }

            // 清理局部引用
            currentEnv->DeleteLocalRef(jbyteArrayData);

            return jsi::Value(static_cast<double>(bufferSize));
        });
    
    // 将函数挂载到JS全局对象
    runtime.global().setProperty(runtime, "receiveDataFromCpp", std::move(receiveDataFunc));
}

3. 全局引用清理(避免内存泄漏)

在模块卸载时,释放缓存的全局引用:

// 模块卸载时调用该方法
void uninstall(JavaVM* vm, jobject module_ref) {
    JNIEnv* env = nullptr;
    vm->AttachCurrentThread(&env, nullptr);
    if (s_rtn_stream_class != nullptr) {
        env->DeleteGlobalRef(s_rtn_stream_class);
        s_rtn_stream_class = nullptr;
    }
    if (module_ref != nullptr) {
        env->DeleteGlobalRef(module_ref);
    }
}

修复效果验证

修复后重复调用receiveDataFromCpp时,JS线程会使用自身绑定的JNIEnv,不会再出现跨线程访问JNIEnv导致的SIGSEGV错误,同时缓存jclass也提升了回调执行效率。

内容的提问来源于stack exchange,提问作者Stéphane R.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 00:14:49