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

使用PJSIP库开发SIP通话时接电话触发JNI崩溃问题求助

解决PJSIP JNI层无效对象导致的接听崩溃问题

我之前在基于PJSIP开发SIP通话功能时,遇到过几乎一模一样的偶发崩溃问题。从你提供的错误日志来看,JNI DETECTED ERROR IN APPLICATION: use of invalid jobject这个报错,核心原因基本是Java对象的生命周期管理混乱,再结合PJSIP天生的多线程架构,很容易在跨线程回调时踩坑。下面是我整理的排查方向和解决方法:

可能的原因及解决方案

1. 局部引用被提前回收,未使用全局引用

PJSIP的回调事件(比如接听请求)是在独立的native线程(日志里的sipStack线程)触发的。如果你在JNI方法里只创建了Java对象的局部引用,当该JNI方法执行完毕返回后,局部引用会被VM自动回收,后续native线程再尝试访问这个对象时,就会出现无效对象错误。

解决方法:

  • 在初始化回调相关的Java对象时,使用NewGlobalRef创建全局引用,将其存储在native层的结构体或全局变量中;
  • 当确认不再需要该对象时(比如通话结束后),调用DeleteGlobalRef手动释放全局引用,避免内存泄漏。

示例代码(C++):

// 假设你有一个Java层的SipListener对象需要在native回调中使用
jobject g_sip_listener = nullptr;

// 在初始化回调时创建全局引用
JNIEXPORT void JNICALL Java_com_xxx_xxx_SipService_setListener(JNIEnv *env, jobject thiz, jobject listener) {
    if (g_sip_listener != nullptr) {
        env->DeleteGlobalRef(g_sip_listener);
    }
    // 创建全局引用
    g_sip_listener = env->NewGlobalRef(listener);
}

// 在通话结束或销毁时释放全局引用
JNIEXPORT void JNICALL Java_com_xxx_xxx_SipService_destroy(JNIEnv *env, jobject thiz) {
    if (g_sip_listener != nullptr) {
        env->DeleteGlobalRef(g_sip_listener);
        g_sip_listener = nullptr;
    }
}

2. 跨线程访问未做同步,对象被Java层GC回收

PJSIP的native线程和Java层的线程(比如UI线程)是独立的,如果Java层的对象没有被强引用持有,很可能被GC提前回收,此时native层再访问就会触发无效对象错误。另外,直接在native线程操作Java对象也可能存在线程安全问题。

解决方法:

  • 在Java层保持对回调对象(比如SipListener、通话管理器)的强引用,避免GC自动回收;
  • 在native回调中,先检查对象是否有效,再执行操作:
JNIEnv* getEnv() {
    JNIEnv* env;
    // 确保当前线程关联到VM
    g_java_vm->AttachCurrentThread(&env, nullptr);
    return env;
}

// 在接听回调中检查对象有效性
void onCallAnswer() {
    JNIEnv* env = getEnv();
    if (env->IsSameObject(g_sip_listener, nullptr)) {
        // 对象已无效,直接返回
        g_java_vm->DetachCurrentThread();
        return;
    }
    // 调用Java层方法处理接听逻辑
    jmethodID method = env->GetMethodID(env->GetObjectClass(g_sip_listener), "onCallAnswered", "()V");
    env->CallVoidMethod(g_sip_listener, method);
    g_java_vm->DetachCurrentThread();
}
  • 如果需要操作UI相关对象,通过Java层的Handler或runOnUiThread切换到UI线程执行,避免跨线程操作风险。

3. 对象生命周期不匹配,native层仍在使用时Java层已销毁

比如接听时创建的音频流对象、通话会话对象,在native层还在处理数据时,Java层已经调用了销毁方法释放这些对象,导致native层访问无效引用。

解决方法:

  • 梳理通话流程中的对象创建、使用、销毁时机,添加状态标记(比如isCallActive),确保native层不再使用该对象后,Java层才执行销毁操作;
  • 可以在native层维护引用计数,当引用计数归0时,再通知Java层回收对象。

总结

这种偶发崩溃的核心是JNI层与Java层的对象生命周期不同步,尤其是多线程场景下的引用管理问题。优先排查全局引用的使用是否正确,再检查对象是否被提前GC回收,最后确认跨线程操作的同步逻辑是否完善。

内容的提问来源于stack exchange,提问作者Jyoti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:26:53