返回
Featured image of post JNI 调用 DLL

JNI 调用 DLL

本文使用环境: 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对应类型
booleanbooljboolean
bytecharjbyte
shortshortjshort
intintjint
longlongjlong
floatfloatjfloat
doubledoublejdouble
charcharjchar

Java 中的对象类型与 C++ 中的类或结构体对应。

Java类型JNI类型
stringjstring
objectjobject
classjclass
ArrayListjobject
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
}

普通数组

对于数组,通常不需要使用 GetFieldIDGetMethodID,因为数组操作通常通过 JNI 提供的专门函数来进行,例如 GetFloatArrayElementsSetFloatArrayRegion 等。

 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);
Licensed under CC BY-ND