JavaNative Interface的缩写,中文为Java本地调用。从Java1.1开始,JNI即成为Java标准的一部分。 数组
JNI设计的目的是为了容许Java代码与其余语言进行交互。但这样作一般会致使丧失平台可移植性,一般是在特定的需求下进行,例如使用旧的其余语言的库、须要得到Java类库不支持的某种基于具体平台的特性、大量数学计算性能优化等。 缓存
1.基本类型 性能优化
JNI基本类型和本地等效类型的对应表格以下: 数据结构
Java类型 函数 |
本地类型 性能 |
说明 优化 |
boolean 编码 |
jboolean spa |
无符号,8位 设计 |
byte |
jbyte |
无符号,8位 |
char |
jchar |
无符号,16位 |
short |
jshort |
有符号,16位 |
int |
jint |
有符号,32位 |
long |
jlong |
有符号,64位 |
float |
jfloat |
32位 |
double |
jdouble |
64位 |
void |
void |
无 |
为了使用方便,还提供了以下定义:
#define JNI_FALSE 0
#define JNI_TRUE 1
Jsize类型用于描述主要指数和大小:
typedef jint jsize;
2.引用类型
除了基本类型外,JNI还包含了很对对应于不一样Java对象的引用类型,JNI引用类型的组织层次以下图所示:
在C语言中,全部其余JNI引用类型都被定义为与jobject同样,例如:
typedef jobject jclass;
在C++中,JNI引入虚构类以增强子类关系,例如:
class _jobject{};
class _jstring : public jobject{};
…
typedef _jobject jobject;
typedef _jstring jstring;
3.方法ID和域ID
方法ID和域ID是常规的C指针类型:
struct_jmethodID; /*不透明结构*/
typedefstruct _jmethodID *jmethodID; /*方法ID*/
struct_jfieldID; /*不透明结构*/
typedefstruct _jfieldID *jfieldID /*域ID*/
4.值类型
jvalue联合在参数数组中用做单元类型,其声明以下:
typedefunion _jvalue
{
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
}jvalue;
5.UTF8字符串
JNI的UTF8字符串与标准UTF8格式有两个区别,第一,空字节0使用双字节格式进行编码,而不是标准UTF8的单字节;第二,只使用单字节、双字节和三字节格式,不支持更长的字节格式。
1. 类型签名
Java虚拟机的类型签名以下:
类型签名 |
Java类型 |
Z |
boolean |
B |
byte |
C |
char |
S |
short |
I |
int |
J |
long |
F |
float |
D |
double |
Lfully-qulitied-class; |
全限定类 |
[type |
type[] 数组 |
(argtypes)rettype |
方法类型 |
例如,Java方法int feet(int n, String s,int [] arr)的类型签名以下:
(ILJava/lang/String;[I)I
圆括号里面为参数,I表示第一个参数int型,LJava/lang/String;表示第二个参数为全限定Java.lang.String类型,[I表示第三个参数为int型的数组,圆括号后面为返回值类型,I表示返回值为int型。
2. 通常函数的JNI接口函数命名方式
通常JNI接口函数命名以下:
Java_包名_类名_方法名。
例如:某工程下Sample/test包下MySigal类的int GetASample()方法的C语言实现函数命名以下:
jint Java_Sample_test_MySigal_GetASample(JNIEnv* env,jobjectobj)
其中,包名所包含的“/”应所有如下划线替代,其本地实现的参数和返回值也应转换为JNI类型。
3. 重载函数的JNI接口函数命名方式
重载函数的JNI实如今通常函数的JNI实现以外,还应添加上类型签名以做为同名函数之间的区别,其接口函数命名以下:
Java_包名_类名_方法名_参数签名。
例如:某工程下Sample/test包下MySigal类的int GetASample(int n, String s,int [] arr)方法的C语言实现函数命名以下:
jintJava_Sample_test_MySigal_GetASample_ILJava_lang_String_2_3I
(JNIEnv*env, jobject obj, jint n, jstring s, jintarray arr)。
JNI在函数命名时采用名字扰乱方案,以保证全部的Unicode字符都能转换为有效的C函数名,全部的“/”,不管是包名中的仍是全限定类名中的,均使用“_”代替,用_0,…,_9来代替转义字符,以下:
转义字符序列 |
表示 |
_0XXXX |
Unicode字符XXXX |
_1 |
字符“_” |
_2 |
签名中的字符“;” |
_3 |
签名中的字符“[” |
在目前的应用中,咱们所主要须要关心的是C/C++数据类型与JNI本地类型之间的转化过程,这个过程某些数据的转换须要使用JNIEnv对象的一系列方法来完成。
1.jstring转换为C风格字符串
char* test = (char*)(*env)->GetStringUTFChars(env,jstring,NULL);
使用完毕后,应调用:
(*env)->ReleaseStringUTFChars(env,jstring, test);
释放资源。
2.C风格字符串转换为jstring
char charStr[50];
jstring jstr;
jstr = env ->NewStringUTF(charStr);
3.C语言中获取的一段char*的buffer传递给Java
在jni中new一个byte数组,而后使用
(*env)->SetByteArrayRegion(env,bytearray, 0, len, buffer)
操做将buffer拷贝到数组中。
这种方式主要是针对buffer中存在“\0”的状况,若是以C风格字符串的方式读入,就会损失“\0”以后的字符。
4.数组操做
数组操做的相关函数列表以下:
JNI函数 |
功能 |
GetArrayLength |
返回数组中的元素数 |
NewObjectArray |
建立一个指定长度的原始数据类型数组 |
GetObjectArrayElement |
返回Object数组的元素 |
SetObjectArrayElement |
设置Object数组的元素 |
GetObjectArrayRegion |
将原始数据类型数组中的内容拷贝到预先分配好的内存缓存中 |
SetObjectArrayRegion |
设置缓存中数组的值 |
ReleaseObjectArrayRegion |
释放GetObjectArrayRegion分配的内存 |
对int,char等基本数据类型的数组操做,将相关Object名称替换为对应基本数据类型名称即为相关函数。
数组操做的方法选择基于使用者的需求而定,若是使用者须要在内存中拷贝数组并对其进行操做那么通常使用GetObjectArrayRegion和SetObjectArrayRegion函数,不然通常使用SetObjectArrayElement和GetObjectArrayElement函数。