JNI定义
JNI是Java程序设计语言功能最强的特征,它允许Java类的某些方法原生实现,同时让它们能够像普通Java方法一样被调用和使用。
方法声明。
原生方法中必须存在的两个参数:JNIEnv*,jobject。第一个参数JNIEnv是指向JNI函数列表的接口指针,第二个参数jobject是获取类引用或实例引用。
JNIEnv接口指针
- C代码中,指向JNINativeInterface结构的指针。
- C++代码中,是C++类实例。
实例方法与静态方法
- 实例方法与类实例相关,只能在类实例中调用。
- 静态方法不与实例相关,可以在静态上下文直接调用。
静态方法和实例方法均可以声明为原生的,可以通过JNI技术以源生代码的形式提供实现。
数据类型
- 基本数据类型:布尔型,字节型,字符型,短整型,整型,长整型,浮点型和双精度浮点型。
Java类型 | JNI类型 | C/C++类型 | 大小 |
---|---|---|---|
boolean | jblloean | unsigned char | 无符号8位 |
bety | jbyte | char | 有符号8位 |
char | jchar | unsigned short | 无符号16位 |
short | jshort | short | 有符号16位 |
int | jnit | int | 有符号32位 |
long | jlong | long long | 有符号64位 |
float | jfloat | float | 32位 |
double | jdouble | double | 64位 |
- 引用类型:与基本数据类型不同,引用类型对原生方法是不透明的,内部数据结构并不直接向原生代码公开。
Java类型 | 原生类型 |
---|---|
java.lang.Class | jclass |
java.lang.Throwable | jthrowable |
java.lang.String | jstring |
Other objects | jobjects |
java.lang.Object[] | jobjectArray |
boolean[] | jbooleanArray |
byte[] | jbyteArray |
char[] | jcharArray |
short[] | jshortArray |
int[] | jintArray |
long[] | jlongArray |
float[] | jfloatArray |
double[] | jdoubleArray |
Other arrays | jarray |
对引用数据类型的操作
引用类型以不透明的引用方式传递给原生代码,而不是以原生数据类型的形式呈现,因此引用类型不能直接使用和修改。
-
字符串操作
- 创建字符串
/*用给定的C字符串创建Java字符串*/ jstring javaString; javaString = (*env)->NewString(env,"Hello World!");
/*将Java字符串转换成C字符串*/ const jbyte* str; jboolean isCopy; srt = (*env)->GetStringUTFChars(env,javaString,&isCopy);
- 通过JNI GetStringChars和GetStringUTFChars函数获得的C字符串在原生代码中使用完之后需要正确的释放,否则会造成内存泄漏。
(*env)->ReleaseStringUTFChars(env,javaString,str);
-
数组操作
- 创建数组
/*在原生代码中创建数组*/ jintArray javaArray; javaArray = (*env)->NewIntArray(env,10);
- 访问数组元素
- 将Java数组区复制到C数组中
jint nativeArray[10]; (*env)->GetIntArrayRegion(env,javaArray,0,10,nativeArray);
- 从C数组向Java数组提交所作的修改
(*env)->SetIntArrayRegion(env,javaArray,0,10,nativeArray);
- 对直接指针的操作
/*获得指向Java数组元素的直接指针*/ jint* nativeDirectArray; jboolean isCopy; nativeDirectArray = (*env)->GetIntArrayElements(env,javaArray,&isCopy);
/*释放指向Java数组元素的直接指针*/ (*env)->ReleaseIntArrayElements(env,javaArray,nativeDirectArray,0);
NIO 操作
原生I/O(NIO)在缓冲管理区,大规模网络和文件I/O及字符集支持方面的性能有所改进。JNI提供了原生代码中使用NIO的函数。
- 创建直接字节缓冲区
unsigned char* buffer = (unsigned char*) malloc(1024);
jobject directBuffer;
directBuffer = (*env)->NewDirectByteBuffer(env,buffer,1024);
原生方法中的内存分配超出了虚拟机的管理范围,且不能用虚拟机的垃圾回收器回收原生方法中的内存。原生方法应该通过释放未使用的内存分配以避免内存泄漏来正确管理内存
- 通过Java字节缓冲区获取原生字节数组
unsigned char* buffer;
buffer = (unsigned char*)(*env)->GetDirectBufferAddress(env,directBuffer);
访问域
- 带有静态域和实例域的Java类
public class JavaClass {
private String instanceField = "Instance Field";
private static String staticField = "static Field";
}
- 获取域ID
- 用对象引用获得类
jclass clazz; clazz = (*env)->GetObjectClass(env,instance);
- 获取实例域的域ID
jfieldID instanceFieldId; staticFieldId = (*env)->GetFieldID(env,clazz,"instanceField","Ljava/lang/String;");
- 获取静态域的域ID
jfieldID staticFieldId; staticFieldId = (*env)->GetStaticFieldID(env,clazz,"staticField","Ljava/lang/String;");
- 获取域
- 获取实例域
jstring instanceFieldStr; instanceField = (*env)->GetObjectField(env,instance,instanceFieldIdStr);
- 获取静态域
jstring staticFieldStr; staticField = (*env)->GetStaticObjectField(env,clazz,staticFieldIdStr);
调用方法
- 带有静态方法和实例方法的Java类
public class JavaClass {
private String instanceMethod(){
return "Instance Method";
}
private static String staticMethod(){
return "Static Method";
}
}
-
获取方法ID
- 获取实例方法的方法ID
jmethodID = instanceMethodId; instanceMethodId = (*env)->GetMethodID(env,clazz,"instanceMethod","()Ljava/lang/String;");
- 获取静态方法的方法ID。
jmethodID staticMethodId; staticMethodId = (*env)->GetStaticMethodID(env,clazz,"staticMethod","()Ljava/lang/String;");
调用方法
- 调用实例方法
(*env)->CallStringMethod(env,instance,instanceMethodId);
- 调用静态方法
(*env)->CallStaticStringMethod(env,instance,staticMethodId );
域和方法描述
- 使用java类文件反汇编程序——javap,解压缩类中的域和方法描述符。