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
相关产品推荐
相关产品推荐

