c++通过 JNI 调用 java 方法需先获取 JNIEnv,再通过 GetObjectClass/FindClass、GetMethodID/GetStaticMethodID、CallObjectMethod/CallStaticObjectMethod 等完成调用,严格处理签名、异常检查及局部引用释放。

在 C ++ 中通过 JNI 调用 Java 方法,核心是获取 Java对象 或类的引用,再通过 JNIEnv 提供的函数(如 GetMethodID、CallObjectMethod 等)完成调用。关键在于正确获取类、方法 ID 和参数处理,尤其注意局部 / 全局引用管理和异常检查。
获取 JNIEnv指针 与jvm环境
Native 代码必须运行在 JVM 已启动的上下文中。常见方式有:
- 从 Java 层回调进入 C ++(如声明
native方法),此时 JNIEnv 由 JVM 自动传入,可直接使用; - 在独立 C ++ 程序中主动 Attach 到 JVM(需持有 JavaVM* 指针),调用
GetEnv或AttachCurrentThread获取 JNIEnv; - Detach 前必须调用
DetachCurrentThread(仅对 Attach 过的 线程)。
调用 Java 实例方法(非静态)
假设 java 类 为com.example.Utils,含方法public String format(String s, int n),且已有该类实例jobject obj:
// 1. 获取类引用(可缓存)jclass cls = env->GetObjectClass(obj); if (!cls) {/* 处理异常 */} <p>// 2. 获取方法 ID(签名需严格匹配)// "format" 是方法名;"(Ljava/lang/String;I)Ljava/lang/String;" 是签名 jmethodID mid = env->GetMethodID(cls, "format", "(Ljava/lang/String;I)Ljava/lang/String;"); if (!mid) {/<em> 方法未找到或异常 </em>/}</p><p>// 3. 构造参数:创建 Java 字符串 jstring jstr = env->NewStringUTF("hello"); if (!jstr) {/<em> 内存不足 </em>/}</p><p>// 4. 调用方法 jstring result = (jstring)env->CallObjectMethod(obj, mid, jstr, 123);</p><p>// 5. 使用结果(如转为 C 字符串)const char* cstr = env->GetStringUTFChars(result, nullptr); if (cstr) {printf("Result: %sn", cstr); env->ReleaseStringUTFChars(result, cstr); // 必须释放 }</p><p>// 6. 清理局部引用(避免引用泄漏)env->DeleteLocalRef(jstr); env->DeleteLocalRef(result); env->DeleteLocalRef(cls);
调用 Java 静态方法
无需 Java 对象实例,改用 GetStaticMethodID 和CallStaticXXXMethod:
立即学习“Java 免费学习笔记(深入)”;
// 获取类(通过类名查找)jclass cls = env->FindClass("com/example/Utils"); if (!cls) {/* 类未找到 */} <p>jmethodID mid = env->GetStaticMethodID(cls, "getVersion", "()Ljava/lang/String;"); if (!mid) {/<em> 异常已抛出 </em>/}</p><p>jstring version = (jstring)env->CallStaticObjectMethod(cls, mid);</p><p>// 后续同上:使用、释放、清理 const char* ver = env->GetStringUTFChars(version, nullptr); if (ver) {printf("Version: %sn", ver); env->ReleaseStringUTFChars(version, ver); } env->DeleteLocalRef(version); env->DeleteLocalRef(cls);
签名生成与常见类型映射
JNI 方法签名决定参数和返回 值类型 ,可用javap -s 查看。常用规则:
- 基本类型:
I(int)、Z(Boolean)、D(double)等; - 对象类型:以
L开头,全限定名用/分隔,结尾加;,如Ljava/lang/String;; - 数组:前置
[,如[I表示int[],[Ljava/lang/Object;表示Object[]; - 方法签名格式:
(参数签名)返回值签名,无参数写()。
例如 void log(String tag, Object…… args) 的签名是:(Ljava/lang/String;[Ljava/lang/Object;)V。
不复杂但容易忽略的是异常检查和引用管理——每次 JNI 调用后建议用 env->ExceptionCheck() 判断是否出错,并及时清理局部引用,否则可能引发内存泄漏或 JVM 崩溃。