jni

JNI数据类型

JNI基本数据类型是Java基本数据类型和C数据类型之间的桥梁;

Java数据类型 –> JNI数据类型 –> C数据类型
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble
void void

引用数据类型

String jstring
object jobject
byte[] jByteArray
int[] jIntArray
object[] jobjectArray

JNI读取Java数据类型对应的签名

image

windows下的jni开发

windows下开发jni流程大致可以为:

1、编写native方法

2、通过javah命令生成.h头文件

3、复制.h头文件到CPP工程下

4、拷贝jni.h和jni_md.h到CPP工程中(在JDK中找)

5、实现.h头文件中的声明函数

6、生成DLL动态库(配置解决方案为x86平台下)

7、将DLL动态库所在位置配置到环境变量中(配置完必须进行重启)

也可以直接拷贝到工程目录下,建立一个jni文件夹,直接引入即可:

static {

    System.loadLibrary(“jni/jni_02”);

}

1、在Java中编写调用Native方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class JniTest {

public native static String getStringFromC();

public static void main(String[] args) {
String result = getStringFromC();
System.out.println(result);
}

static {
// 输出加载库的地址
System.out.println( System.getProperty("java.library.path"));
System.loadLibrary("jni_02");
}
}

2、命令行进入src目录下,使用javah命令:

1
javah com.my.jnitest.JniTest

3、CPP工程下,添加jni.h和jni_md.h和生成的com_my_jnitest_JniTest.h头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_my_jnitest_JniTest */

#ifndef _Included_com_my_jnitest_JniTest
#define _Included_com_my_jnitest_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_my_jnitest_JniTest
* Method: getStringFromC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_my_jnitest_JniTest_getStringFromC
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

注意此处引入jni.h头文件时,使用”jni.h”引入,因为该头文件并不是系统自带的,而是我们自己引入的。

4、源文件夹下编写实现对应的头文件中声明的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "com_my_jnitest_JniTest.h"

/*
* Class: com_my_jnitest_JniTest
* Method: getStringFromC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_my_jnitest_JniTest_getStringFromC
(JNIEnv * env, jclass jclass){
// 如果Java中定义的是static方法,则此处是Java,表示Java类
// 如果Java中定义的是实例方法,则此处是jobject,表示调用的类实例
// jclass 和 jobject其实是相同的,都表示_jobject的指针,jclass是jobject的别名

// JNIEnv是结构体指针JNINativeInterface_ * 的别名
// JNINativeInterface_ 是一个结构体,其定义了一套与Java交互的方法

// 由于JNIEnv本身就是一个指针变量,所以此处的env是一个二级指针

// 将C字符串转为Java字符串
//return (**env).NewStringUTF(env,"String from C");
return (*env)->NewStringUTF(env, "String from C");
}

5、配置解决方案,并生成

image

6、将生成的dll文件放到指定目录下(已经配置在环境变量中);

最后,java代码运行,会在环境变量中找到对应dll文件的目录,让后加载;

JNIEnv

jni函数中,都必须有JNIEnv指针变量作为参数,这个变量在C和C++环境下是不同的;

1
2
3
4
5
6
7
JNIEXPORT jstring JNICALL Java_com_my_jnitest_JniTest_getStringFromC
(JNIEnv * env, jclass jclass){
// C调用方式
return (*env)->NewStringUTF(env, "String from C");
// C++调用方式
return env->NewStringUTF("String from C");
}

C环境下:

JNIEnv是结构体指针JNINativeInterface_ * 的别名;
JNINativeInterface_ 是一个结构体,其定义了一套与Java交互的函数;
由于JNIEnv本身就是一个指针变量,所以此处的env是一个二级指针;

C++环境下:

在C++ 中,JNIEnv就是一个结构体,所以这里env表示的只是一个一级指针;

C++ 中实际还是调用C中的JNINativeInterface_中对应的方法,也就是说,只是对JNINativeInterface_
的包装,C++ 中的struct类似于Java类,带有this引用;而这个this引用表示的是调用的上下文环境
,即调用方法的JNINativeInterface_ * 指针;

C++的JNIEnv实际是JNIEnv_结构体:

1
2
3
4
5
6
7
8
9
struct JNIEnv_ {
const struct JNINativeInterface_ *functions;
#ifdef __cplusplus

jint GetVersion() {
return functions->GetVersion(this);
}
...
}

为什么C++中env是一级指针,而C中是二级指针?

1、首先,struct并不像类一样,作为参数时,其进行的是值传递,而不是引用传递,为了保证jni函数
全局操作的是同一个JNIEnv,所以函数参数env必须是一个指针;

2、在C中,查看JNIEnv的声明,其本身就是一个JNINativeInterface_ * 指针,按理来说,env参数
不需要是一个指针参数(按理来说,只需是普通变量JNIEnv env),但由于在C++ 定义中,JNIEnv并
不是一个指针,而是结构体JNIEnv_的别名,为了兼容C++,达到第一点说的引用传递的效果,env参数
必须是一个指针;

这也就是为什么,在C++中JNIEnv * env是一级指针,而C中是二级指针,C中是为了兼容C++,否则
使用一级指针即可达到目的;