本文使用环境: java sdk 1.8 + visual studio 2017
java生成头文件
需声明待调用的dll中的方法为native方法
1
2
3
4
5
6
7
8
| // testDll.java
public class testDll
{
static {
System.loadLibrary("testDll"); // dll名不需要加.dll的后缀
}
public native List<List<float[]>> getValue(float[] nodeData, int nodeSize, String csvPath, String columnName) throws Exception;
}
|
控制台使用javac(生成.class文件)和javah(生成.h文件)命令以生成.h文件。
例如上述testDll.java在本级目录cmd命令为
1
2
| javac testDll.java
javah testDll
|

C++构建dll工程
Visual Studio新建DLL工程。注意,工程名必须与java约定的dll名一致。

工程的附加包含目录需添加jni.h、jni_md.h所在的目录。

将java生成的头文件加入工程,为其实现.cpp文件即可。

JNI类型对应
Java 基本类型与 C++ 基本类型的对应关系是通过 JNI 提供的原生类型(如 jint, jfloat 等)来实现的。
| Java类型 | C++类型 | JNI对应类型 |
|---|
| boolean | bool | jboolean |
| byte | char | jbyte |
| short | short | jshort |
| int | int | jint |
| long | long | jlong |
| float | float | jfloat |
| double | double | jdouble |
| char | char | jchar |
Java 中的对象类型与 C++ 中的类或结构体对应。
| Java类型 | JNI类型 |
|---|
| string | jstring |
| object | jobject |
| class | jclass |
| ArrayList | jobject |
| int[] | jintArray |
| float[] | jfloatArray |
| double[] | jdoubleArray |
| long[] | jlongArray |
| object[] | jobjectArray |
JNI常用函数
调用对象类方法
JNI调用对象类的函数使用逻辑大致为:获取到类(FindClass)→获取方法id(GetMethodId)→调用方法(Call***Method, ***取决于类型)【均通过JNIEnv * env操作】
String
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // 获取 String 类
jclass stringClass = env->FindClass("java/lang/String");
if (stringClass == NULL) {
env->ExceptionDescribe();
return NULL;
}
// 获取 String 类的构造方法 String(String)
jmethodID stringConstructor = env->GetMethodID(stringClass, "<init>", "(Ljava/lang/String;)V");
if (stringConstructor == NULL) {
env->ExceptionDescribe();
return NULL;
}
// 获取 String 类的实例方法 length()
jmethodID lengthMethod = env->GetMethodID(stringClass, "length", "()I");
if (lengthMethod == NULL) {
env->ExceptionDescribe();
return NULL;
}
// 调用构造方法创建一个 String 对象
jstring inputString = env->NewStringUTF("Hello, JNI!");
jobject myString = env->NewObject(stringClass, stringConstructor, inputString);
// 调用 length() 方法获取字符串长度
jint length = env->CallIntMethod(myString, lengthMethod);
|
ArrayList
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| // 获取 ArrayList 类
jclass arrayListClass = env->FindClass("java/util/ArrayList");
if (arrayListClass == NULL) {
env->ExceptionDescribe();
return NULL;
}
// 获取 ArrayList(int) 构造方法,用于指定初始容量
jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "<init>", "(I)V");
if (arrayListConstructor == NULL) {
env->ExceptionDescribe();
return NULL;
}
// 设定初始容量
int initialCapacity = 10;
// 创建一个指定初始容量的 ArrayList
jobject arrayListObj = env->NewObject(arrayListClass, arrayListConstructor, initialCapacity);
if (arrayListObj == NULL) {
env->ExceptionDescribe();
return NULL;
}
// 向 ArrayList 中添加元素
jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
if (addMethod == NULL) {
env->ExceptionDescribe();
return NULL;
}
// 创建字符串元素并添加到 ArrayList
jstring element1 = env->NewStringUTF("Element 1");
env->CallBooleanMethod(arrayListObj, addMethod, element1);
env->DeleteLocalRef(element1);
jstring element2 = env->NewStringUTF("Element 2");
env->CallBooleanMethod(arrayListObj, addMethod, element2);
env->DeleteLocalRef(element2);
|
Exception
抛出异常
1
2
3
4
5
6
7
8
9
10
| jclass exceptionClass = env->FindClass("java/lang/RuntimeException");
if (exceptionClass == NULL) {
return NULL; // 如果找不到异常类,直接返回
}
// 创建异常消息
const char* msg = "An error occurred in JNI";
// 使用 ThrowNew 抛出异常
env->ThrowNew(exceptionClass, msg);
|
捕获和处理 Java 层抛出的异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // 假设我们有一些 JNI 操作,可能会抛出异常
jmethodID methodID = env->GetMethodID(someClass, "someMethod", "()V");
if (methodID == NULL) {
env->ExceptionDescribe(); // 打印异常信息
env->ExceptionClear(); // 清理异常状态
return NULL;
}
env->CallVoidMethod(someObject, methodID);
// 检查是否发生了异常
if (env->ExceptionOccurred()) {
env->ExceptionDescribe(); // 打印异常信息
env->ExceptionClear(); // 清理异常状态
return NULL; // 处理异常,返回 NULL
}
|
普通数组
对于数组,通常不需要使用 GetFieldID 或 GetMethodID,因为数组操作通常通过 JNI 提供的专门函数来进行,例如 GetFloatArrayElements、SetFloatArrayRegion 等。
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 创建一个 float 数组
jfloatArray floatArray = env->NewFloatArray(5);
jfloat values[5] = { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f };
// 将数据写入数组
env->SetFloatArrayRegion(floatArray, 0, 5, values);
// 获取数组中的元素
jfloat* arrayElements = env->GetFloatArrayElements(floatArray, NULL);
for (int i = 0; i < 5; i++) {
printf("Element at index %d: %f\n", i, arrayElements[i]);
}
env->ReleaseFloatArrayElements(floatArray, arrayElements, 0);
|