1.C++层调用Java:
在C++中调用Java方法是通过JNI提供的接口实现的。这种调用的过程涉及以下几个步骤:
- 获取
JNIEnv
指针:这是调用Java方法的核心接口。 - 定位Java类:通过JNI提供的方法,找到你要调用的方法所在的Java类。
- 获取方法ID:通过方法名和签名来获取Java方法的ID。
- 调用Java方法:使用获取的方法ID来调用相应的Java方法。
- 处理返回值:如果Java方法有返回值,处理它。
下面是一个具体的例子,演示如何在C++中调用Java方法。
示例场景
假设我们有一个Java类 JavaClass
,其中包含一个方法 sayHello
和一个静态方法 addNumbers
。
Java代码
java
package com.example;
public class JavaClass {
public void sayHello() {
System.out.println("Hello from Java!");
}
public static int addNumbers(int a, int b) {
return a + b;
}
}
C++代码调用Java方法
在C++代码中,我们要调用JavaClass
类的 sayHello
实例方法和 addNumbers
静态方法。
C++代码
cpp
#include <jni.h>
#include <iostream>
// 一个例子函数,用于调用Java方法
void callJavaMethod(JNIEnv *env) {
// 1. 获取Java类的引用
jclass javaClass = env->FindClass("com/example/JavaClass");
if (javaClass == nullptr) {
std::cerr << "Failed to find Java class" << std::endl;
return;
}
// 2. 调用实例方法 sayHello
// 获取方法ID
jmethodID sayHelloMethod = env->GetMethodID(javaClass, "sayHello", "()V");
if (sayHelloMethod == nullptr) {
std::cerr << "Failed to find method sayHello" << std::endl;
return;
}
// 创建Java类的实例
jobject javaObject = env->AllocObject(javaClass);
if (javaObject == nullptr) {
std::cerr << "Failed to create Java object" << std::endl;
return;
}
// 调用实例方法
env->CallVoidMethod(javaObject, sayHelloMethod);
// 3. 调用静态方法 addNumbers
// 获取静态方法ID
jmethodID addNumbersMethod = env->GetStaticMethodID(javaClass, "addNumbers", "(II)I");
if (addNumbersMethod == nullptr) {
std::cerr << "Failed to find static method addNumbers" << std::endl;
return;
}
// 调用静态方法
jint result = env->CallStaticIntMethod(javaClass, addNumbersMethod, 3, 7);
std::cout << "Result from Java addNumbers: " << result << std::endl;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
// 在加载时调用Java方法
callJavaMethod(env);
return JNI_VERSION_1_6;
}
详细解析
-
获取Java类的引用 :使用
FindClass
方法来查找Java类。类名需要使用完整的包名,且用斜杠/
分隔。 -
获取方法ID:
- 使用
GetMethodID
来获取实例方法的ID。需要提供方法名和方法签名。 - 使用
GetStaticMethodID
来获取静态方法的ID。签名格式为(参数类型)返回类型
,例如(II)I
表示两个int
参数,返回一个int
。
- 使用
-
创建Java类的实例 :使用
AllocObject
创建一个Java对象实例。然后,使用CallVoidMethod
调用实例方法。 -
调用静态方法 :直接使用
CallStaticIntMethod
调用静态方法,并传递参数。 -
处理返回值 :静态方法
addNumbers
返回一个int
,所以C++代码中捕获并输出这个结果。
注意事项
- 确保正确处理异常。使用
ExceptionCheck
或ExceptionOccurred
来检查Java方法调用中是否出现异常。 - 调用完成后释放资源,比如释放局部引用
DeleteLocalRef
。
通过这些步骤,你可以在C++代码中灵活地调用Java方法,利用JNI在两个语言之间传递数据和功能。
2.java层调用C++层:
java层声明native函数,这个函数由Jni(C++)层实现,java层是如何通过native函数找到C++层的函数的?
需要把C++这些函数要注册到jni中,jni方法注册分为静态注册和动态注册:
1. 静态注册
通过Java中的native方法与C/C++代码中的JNI函数名称相匹配来实现的。静态注册要求C++代码中的函数名称与Java类中的native方法按照一定的规则命名,并自动进行绑定。这就是"静态"的含义,不需要额外的注册代码。
静态注册有明显缺点的:
- JNI层的函数名太长;
- 声明Native方法的类需要用javah工具生成头文件;
- 第一次调用Native方法需要建立关联,影响运行效率;
2.JNI 动态注册
JNI 动态注册是通过在运行时将 Java native
方法与本地(C/C++)代码中的方法进行绑定。与静态注册不同,动态注册不要求本地函数名称遵循特定的命名规则,允许你在代码中手动注册方法。这种方式提供了更大的灵活性,尤其是在处理大型项目或多模块项目时非常有用。
动态注册的工作流程
-
定义 Java
native
方法 :在 Java 类中声明native
方法,但不需要关心 C/C++ 代码中的函数名称。 -
实现 JNI_OnLoad 函数 :在加载本地库时,JVM 会调用
JNI_OnLoad
函数。在这个函数中,你可以注册需要与native
方法绑定的 C/C++ 函数。 -
使用
RegisterNatives
函数注册本地方法 :通过RegisterNatives
函数将 Java 的native
方法与对应的 C/C++ 函数进行绑定。
动态注册的例子
Java 代码:
java
package com.example;
public class MyClass {
static {
System.loadLibrary("mylib"); // 加载本地库
}
// 声明两个native方法
public native int addNumbers(int a, int b);
public native String getGreeting(String name);
public static void main(String[] args) {
MyClass obj = new MyClass();
int result = obj.addNumbers(5, 10);
System.out.println("Result: " + result);
String greeting = obj.getGreeting("John");
System.out.println("Greeting: " + greeting);
}
}
C/C++ 代码:
c
#include <jni.h>
#include <string.h>
// 实现两个本地方法
jint addNumbers(JNIEnv *env, jobject obj, jint a, jint b) {
return a + b;
}
jstring getGreeting(JNIEnv *env, jobject obj, jstring name) {
const char *nameStr = (*env)->GetStringUTFChars(env, name, 0);
char greeting[100];
sprintf(greeting, "Hello, %s!", nameStr);
(*env)->ReleaseStringUTFChars(env, name, nameStr);
return (*env)->NewStringUTF(env, greeting);
}
// 定义方法数组,将Java方法名和C函数进行绑定
static JNINativeMethod methods[] = {
{"addNumbers", "(II)I", (void *)addNumbers},
{"getGreeting", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getGreeting}
};
// 在JNI_OnLoad中注册这些本地方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
// 获取Java类
jclass cls = (*env)->FindClass(env, "com/example/MyClass");
if (cls == NULL) {
return JNI_ERR;
}
// 注册本地方法
if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}