能实现,但Java↔C++相互调用机制不对称:Java→C++需native声明+loadLibrary加载so/dll,C++→Java需查类、获方法ID、构造对象再调用,二者生命周期、错误处理完全不同。

能实现,但“相互调用”不是对称操作:Java → C++ 是通过 native 方法声明 + 动态链接;C++ → Java 是主动查类、找方法、构造对象、再调用——两者机制、生命周期、错误路径完全不同,不能套用同一套逻辑去写。
Java 调用 C++ 函数必须声明 native 并加载 so/dll
Java 层必须显式声明 native 方法,并在运行前调用 System.loadLibrary("xxx")。JVM 会按命名规则(如 Java_pkg_Class_method)查找对应符号。
- 包名
com.example.MyClass对应头文件中类名部分为com_example_MyClass,不能手写错斜杠或下划线 -
javah已废弃,推荐用javac编译后执行javap -s ClassName确认签名,再手写或用工具生成头文件 - Windows 下加载
.dll需确保其依赖的msvcp140.dll等运行时在 PATH 中;Android 下只支持.so,且 ABI(armeabi-v7a/arm64-v8a)必须匹配 - 未正确
loadLibrary就调用native方法,抛出UnsatisfiedLinkError,不是 JNI 层崩溃,而是 JVM 拦截的 Java 异常
C++ 调用 Java 实例方法三步缺一不可:jobject + GetMethodID + CallXXXMethod
漏掉任一环节,或顺序/类型不匹配,大概率直接 crash 或返回垃圾值,而不是抛 Java 异常。
-
jobject必须是有效本地引用:不能是已DeleteLocalRef过的,也不能跨线程复用(除非先AttachCurrentThread) -
GetMethodID的签名必须一字不差:Ljava/lang/String;写成Ljava/lang/string;(小写 s)就失败;泛型擦除后只认Ljava/util/List;,不认Ljava/util/List<ljava>;</ljava> - 调用后必须检查异常:
if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); },否则后续任意 JNI 调用可能静默失败 - 不要用
GetObjectClass反推类来查方法 ID——它返回的是实际运行时类(比如子类),而你期望的方法可能只在父类声明,导致GetMethodID返回NULL
Android 上 FindClass 在非主线程必须先 AttachCurrentThread
这是 Android 特有坑:主线程默认 attached,其他线程首次调用 FindClass 前不 AttachCurrentThread,一定返回 NULL,且无日志提示。
立即学习“Java免费学习笔记(深入)”;
- 每次 attach 后记得配对
DetachCurrentThread,否则线程退出时 JVM 不清理资源,长期运行会泄漏 - 如果只是临时调用 Java 方法,用完立刻 detach;若需频繁交互,可缓存
JNIEnv*并管理 attach/detach 生命周期 -
FindClass返回的是 local ref,若需跨多次 JNI 调用使用,必须转成 global ref:jclass cls = env->NewGlobalRef(env->FindClass("java/lang/String")); - 别缓存
GetMethodID到全局变量里直接用——类卸载(热修复、插件化)后该 ID 失效,应改为静态局部变量 + 判空重取
基本类型和对象参数传参规则不一致
JNI 不自动转换,所有参数都得手动适配。传错类型不会编译报错,但运行时行为不可控。
-
jstring不能直接传 C 字符串指针:env->NewStringUTF("hello")(仅限修改版 UTF-8)或env->NewString((const jchar*)L"hello", 5)(UCS-2) - 传
byte[]:先env->NewByteArray(len),再env->SetByteArrayRegion(arr, 0, len, (jbyte*)data) - 传
String[]:循环新建每个jstring,再用env->NewObjectArray(size, stringClass, NULL),然后逐个env->SetObjectArrayElement - Java 方法参数是
int[],C++ 侧必须传jintArray,不能传jint*;同理boolean对应jboolean,不是bool
最易被忽略的是异常检查和线程绑定:C++ 主动调 Java 时,哪怕只调一次 CallVoidMethod,也必须紧接着 ExceptionCheck;Android 非主线程调 FindClass 前不 attach,必挂,而且挂得无声无息。


















