这些概念脑子过一遍 , 实际上是记不住的 , 只能记录下来查缺补漏 , 通过实战去强化记忆。
JNI的设计原理和代码示例
JNI是Java Native Interface的缩写,是Java提供的一套标准接口,可以让Java代码和其他语言(如C或C++)编写的代码进行交互。JNI定义了一套规范,包括数据类型、函数签名、命名规则等,以保证不同语言之间的兼容性和可移植性。
JNI的设计原理主要包括以下几个方面:
1. JNIEnv
JNI使用了一个中间层,即JNIEnv,来封装Java虚拟机(JVM)和本地代码之间的通信。JNIEnv是一个指向函数表的指针,提供了大量的JNI函数,可以让本地代码访问和操作JVM中的对象、方法、字段等。JNIEnv是线程相关的,每个线程都有自己的JNIEnv,不能在不同线程之间共享。
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj){ // 使用env来访问和操作JVM }
2. JNI数据类型
JNI使用了一些特殊的数据类型,来表示Java中的数据类型在本地代码中的映射。对于基本类型,JNI和Java之间的映射是一对一的,例如Java中的int类型对应于C/C++中的jint类型。对于引用类型,JNI把Java中的对象当作一个C指针传递到本地函数中,这个指针指向JVM中的内部数据结构,而内部数据结构在内存中的存储方式是不可见的,本地代码必须通过JNIEnv中选择适当的JNI函数来操作JVM中的对象。
JNIEXPORT jint JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jint x, jint y){ // 使用jint来表示Java中的int类型 return x + y; }
3. JNI标识符
JNI使用了一些特殊的标识符,来表示Java中的方法和字段在本地代码中的引用。这些标识符分别是jmethodID和jfieldID,它们通常是指向内部运行时数据结构的指针。本地代码需要通过JNIEnv中提供的函数来获取这些标识符,然后才能调用或访问相应的方法或字段。
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj){ jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "methodName", "(I)V"); if (mid == NULL) { return; /* method not found */ } (*env)->CallVoidMethod(env, obj, mid, 123); }
4. JNI方法签名
JNI使用了一个特殊的字符串格式,来表示Java中方法的签名。这个字符串格式包含了方法参数和返回值的类型信息,以便本地代码能够正确地调用Java方法。例如,一个Java方法public int add(int x, int y)在JNI中的签名是"(II)I",表示该方法有两个int类型的参数和一个int类型的返回值。
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj){ jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "add", "(II)I"); if (mid == NULL) { return; /* method not found */ } jint result = (*env)->CallIntMethod(env, obj, mid, 1, 2); }
以上代码示例展示了如何在C/C++代码中使用JNIEnv、JNI数据类型、JNI标识符和JNI方法签名。
JNI调用Java方法和Java调用JNI方法的流程:
5.JNI的数据类型
JNI的数据类型主要分为两类:基本类型和引用类型。
1. 基本类型
基本类型是指Java中的八种基本数据类型,包括int、boolean等。JNI的基本类型和Java中的基本类型是一一对应的,
它在C/C中都有相应的别名,以j开头,例如jint表示int类型,jboolean表示boolean类型等。JNI的基本类型和C/C中的基本类型在内存中的表示方式是相同的,所以可以直接进行赋值和运算,不需要进行转换。
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jint x, jboolean y){ // 使用jint和jboolean来表示Java中的int和boolean类型 // 可以直接进行赋值和运算 jint z = x + 1; jboolean b = !y; }
2. 引用类型
引用类型是指Java中的对象、数组、字符串等。JNI的引用类型和Java中的引用类型是不完全对应的,它们在C/C中都是指针类型,以j开头,例如jobject表示任意对象类型,jstring表示字符串类型等。JNI的引用类型和C/C中的指针类型在内存中的表示方式是不同的,所以不能直接进行赋值和运算,需要通过JNIEnv中提供的函数来进行转换和操作。
JNIEXPORT void JNICALL Java_ClassName_MethodName (JNIEnv *env, jobject obj, jstring str){ // 使用jstring来表示Java中的String类型 // 需要通过JNIEnv中提供的函数来进行转换和操作 const char *c_str = (*env)->GetStringUTFChars(env, str, 0); // 使用c_str (*env)->ReleaseStringUTFChars(env, str, c_str); }
下表列出了JNI中常用的数据类型及其含义:
JNI数据类型 |
Java数据类型 |
C/C++数据类型 |
JNI签名字符 |
内存大小(字节)64/32位系统 |
含义 |
jboolean |
boolean |
unsigned char |
Z |
1 |
布尔值 |
jbyte |
byte |
signed char |
B |
1 |
字节 |
jchar |
char |
unsigned short |
C |
2 |
字符 |
jshort |
short |
short |
S |
2 |
短整数 |
jint |
int |
int |
I |
4 |
整数 |
jlong |
long |
long long |
J |
8 |
长整数 |
jfloat |
float |
float |
F |
4 |
单精度浮点数 |
jdouble |
double |
double |
D |
8 |
双精度浮点数 |
jobject |
Object |
_jobject* |
L |
8/ 4 |
任意对象 |
jclass |
Class<?> |
_jclass* |
L |
8/ 4 |
类对象 |
jstring |
String |
_jstring* |
Ljava/lang/String; |
8/ 4 |
字符串对象 |
jarray |
Object[] |
_jarray* |
[ |
8/ 4 |
任意数组 |
jbooleanArray |
boolean[] |
_jbooleanArray* |
[Z |
8/ 4 |
布尔数组 |
jbyteArray |
byte[] |
_jbyteArray* |
[B |
8/ 4 |
字节数组 |
jcharArray |
char[] |
_jcharArray* |
[C |
8/ 4 |
字符数组 |
jshortArray |
short[] |
_jshortArray* |
[S |
8/ 4 |
短整数数组 |
jintArray |
int[] |
_jintArray* |
[I |
8/ 4 |
整数数组 |
jlongArray |
long[] |
_jlongArray* |
[J |
8/ 4 |
长整数数组 |
jfloatArray |
float[] |
_jfloatArray* |
[F |
8/ 4 |
单精度浮点数数组 |
jdoubleArray |
double[] |
_jdoubleArray* |
[D |
8/ 4 |
双精度浮点数数组 |
在JNI签名中,基本类型由单个字符表示,对象类型由'L'开始,由';'结束,数组类型由'['开始。例如,jstring在JNI签名中表示为"Ljava/lang/String;",表示它是一个String对象。
请注意,引用类型(如jobject、jclass、jstring等)在内存中的大小取决于你的系统是32位还是64位。在32位系统中,所有的引用类型都是4字节;在64位系统中,所有的引用类型都是8字节。