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

JNI中FindClass方法触发Fatal Signal 11崩溃问题求助

解决JNI调用FindClass崩溃的问题

我之前做Android FFmpeg开发时也踩过这个坑,大概率是JNI线程环境或类引用处理不当导致的,咱们一步步来排查解决:

1. 核心问题:JNIEnv的线程关联性

FFmpeg的回调(比如你这里的onFinish)几乎都是在它自己创建的子线程里执行的,而JNIEnv是线程专属的——你在Java层调用JNI方法时拿到的JNIEnv,只能在当前线程使用,跨线程直接用就会触发崩溃,FindClass就是重灾区。

解决办法:保存全局JavaVM实例,动态获取线程专属JNIEnv

首先在JNI初始化时保存JavaVM(它是全局共享的),然后在回调线程里通过它Attach到当前线程,拿到可用的JNIEnv:

// 全局变量保存JavaVM和监听器类的全局引用
JavaVM *g_global_jvm = NULL;
jclass g_listener_global_cls = NULL;

// JNI加载时初始化全局变量
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    g_global_jvm = vm;
    JNIEnv *env = NULL;
    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        LOGI("Failed to get JNIEnv in JNI_OnLoad");
        return JNI_ERR;
    }

    // 找到监听器类,注意类路径用斜杠分隔(比如com/example/MyVideoListener)
    jclass local_cls = (*env)->FindClass(env, "com/example/MyVideoListener");
    if (local_cls == NULL) {
        LOGI("Can't find listener class");
        return JNI_ERR;
    }
    // 创建全局引用,避免类被GC回收,跨线程也能使用
    g_listener_global_cls = (*env)->NewGlobalRef(env, local_cls);
    // 释放局部引用
    (*env)->DeleteLocalRef(env, local_cls);

    return JNI_VERSION_1_6;
}

然后修改你的onFinish函数,正确获取JNIEnv并调用静态方法:

void onFinish() {
    LOGI("onFinish called in C code ");
    JNIEnv *env = NULL;
    // Attach当前线程到JavaVM,获取专属JNIEnv
    jint attach_result = (*g_global_jvm)->AttachCurrentThread(g_global_jvm, &env, NULL);
    if (attach_result != JNI_OK) {
        LOGI("Failed to attach current thread to JavaVM");
        return;
    }

    // 检查全局类引用是否有效
    if (g_listener_global_cls == NULL) {
        LOGI("Listener class global reference is null");
        goto detach_thread;
    }

    // 查找静态方法,注意方法签名要完全匹配(用javap -s命令生成)
    jmethodID finish_mid = (*env)->GetStaticMethodID(env, g_listener_global_cls, "onVideoFinish", "()V");
    if (finish_mid == NULL) {
        LOGI("Can't find static method onVideoFinish");
        goto detach_thread;
    }

    // 调用静态方法
    (*env)->CallStaticVoidMethod(env, g_listener_global_cls, finish_mid);

    // 检查并处理JNI异常(避免崩溃)
    if ((*env)->ExceptionCheck(env)) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
    }

detach_thread:
    // 用完必须Detach线程,否则会内存泄漏
    (*g_global_jvm)->DetachCurrentThread(g_global_jvm);
}

2. 其他可能的崩溃原因

  • 类路径写错:FindClass的参数必须是完整的类名,用斜杠分隔包名(比如com/example/MyPlayer,不能用.)。
  • 方法签名不匹配:静态方法的签名要完全正确,比如无参无返回值是()V,如果有参数要对应类型(比如(Ljava/lang/String;)V)。可以用javap -s -p 你的类全路径命令生成准确签名。
  • 代码混淆:如果开启了ProGuard,你的监听器类或静态方法可能被混淆,导致FindClass/GetStaticMethodID失败。需要在proguard-rules.pro里添加规则:
    -keep class com.example.MyVideoListener {
        public static void onVideoFinish();
    }
    

3. 调试技巧

开启JNI日志输出,在logcat里过滤JNI标签,能看到更详细的错误信息(比如是找不到类还是找不到方法),方便定位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:57:30