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) { /* 处理异常 */ }
// 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) { / 方法未找到或异常 / }
// 3. 构造参数:创建Java字符串
jstring jstr = env->NewStringUTF("hello");
if (!jstr) { / 内存不足 / }
// 4. 调用方法
jstring result = (jstring)env->CallObjectMethod(obj, mid, jstr, 123);
// 5. 使用结果(如转为C字符串)
const char* cstr = env->GetStringUTFChars(result, nullptr);
if (cstr) {
printf("Result: %s\n", cstr);
env->ReleaseStringUTFChars(result, cstr); // 必须释放
}
// 6. 清理局部引用(避免引用泄漏)
env->DeleteLocalRef(jstr);
env->DeleteLocalRef(result);
env->DeleteLocalRef(cls);
调用Java静态方法
无需Java对象实例,改用GetStaticMethodID和CallStaticXXXMethod:
立即学习“Java免费学习笔记(深入)”;
// 获取类(通过类名查找)
jclass cls = env->FindClass("com/example/Utils");
if (!cls) { /* 类未找到 */ }
jmethodID mid = env->GetStaticMethodID(cls, "getVersion", "()Ljava/lang/String;");
if (!mid) { / 异常已抛出 / }
jstring version = (jstring)env->CallStaticObjectMethod(cls, mid);
// 后续同上:使用、释放、清理
const char* ver = env->GetStringUTFChars(version, nullptr);
if (ver) {
printf("Version: %s\n", 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崩溃。










