JNI 数据类型转换

  • Post author:
  • Post category:其他




1、基本数据类型


下面一张表是描述了 Java 基本数据类型和JNI中基本数据类型的相对应关系已经占用空间大小。

JNI 数据类型转换 - hero_213 - hero_213的博客


随便观察就能发现,其实就基本数据类型而已,JNI基本数据类型只是比Java基本数据类型前多了个’j’而已




当然,JNI中还有个Java中没有的


jsize


,定义如下:




typedef jint jsize;




其实jsize整型是用来描述基本指标和大小,没有什么神秘的。

2、引用数据类型


JNI 引用类型 与 Java 的对应关系 如下 树层次图:

JNI 数据类型转换 - hero_213 - hero_213的博客






跟java一样,jobject 是所有引用类型的 父亲。看看是不是很容易理解 ?呵呵.关于jthrowable,JNI也是有线程概念的.




3、        The jvalue Type




Jvalue 是基本数据类型和引用数据类型的集合,它的定义如下:




typedef union jvalue {





jboolean z;




jbyte    b;




jchar    c;




jshort   s;




jint     i;




jlong    j;




jfloat   f;




jdouble  d;




jobject  l;




} jvalue;









Java

类型和本地类型对应


在如下情况下,需要在本地方法中应用java对象的引用,就会用到类型之间的转换:


1)java方法里面将参数传入本地方法;


2)在本地方法里面创建java对象;


3)在本地方法里面return结果给java程序。


分为如下两种情况:



Java

原始类型


像booleans、integers、floats等从Java程序中传到本地方法中的原始类型可以直接使用,下面是java中的原始类型和本地方法中的类型的对应:


Java类型  本地类型  字节(bit)


boolean   jboolean   8, unsigned


byte    jbyte    8


char    jchar    16, unsigned


short    jshort    16


int     jint     32


long    jlong    64


float    jfloat    32


double   jdouble   64


void    void     n/a


也就是说如果我在方法中传进去了一个boolean的参数的话,那么我在本地方法中就有jboolean类型与之对应。同理,如果在本地方法中return一个jint的话,那么在java中就返回一个int类型。


Java对象


Java对象做为引用被传递到本地方法中,所有这些Java对象的引用都有一个共同的父类型jobject(相当于java中的Object类是所有类的父类一样)。下面是JNI实现的一些jobject的子类:


4.本地方法中访问java程序中的内容


1)访问String对象:


从java程序中传过去的String对象在本地方法中对应的是jstring类型,jstring类型和c中的char*不同,所以如果你直接当做 char*使用的话,就会出错。因此在使用之前需要将jstring转换成为c/c++中的char*,这里使用JNIEnv的方法转换。下面是一个例子:


代码3:


JNIEXPORT jstring JNICALL Java_Prompt_getLine


(JNIEnv *env, jobject obj, jstring prompt)


{



char buf[128];


const char *str = (*env)->GetStringUTFChars(env, prompt, 0);


printf(“%s”, str);


(*env)->ReleaseStringUTFChars(env, prompt, str);


这里使用GetStringUTFChars方法将传进来的prompt(jstring类型)转换成为UTF-8的格式,就能够在本地方法中使用了。


注意:在使用完你所转换之后的对象之后,需要显示调用ReleaseStringUTFChars方法,让JVM释放转换成UTF-8的string的对象的空间,如果不显示的调用的话,JVM中会一直保存该对象,不会被垃圾回收器回收,因此就会导致内存溢出。


下面是访问String的一些方法:


◆GetStringUTFChars将jstring转换成为UTF-8格式的char*


◆GetStringChars将jstring转换成为Unicode格式的char*


◆ReleaseStringUTFChars释放指向UTF-8格式的char*的指针


◆ReleaseStringChars释放指向Unicode格式的char*的指针


◆NewStringUTF创建一个UTF-8格式的String对象


◆NewString创建一个Unicode格式的String对象


◆GetStringUTFLengt获取UTF-8格式的char*的长度


◆GetStringLength获取Unicode格式的char*的长度


2) 访问Array对象:


和String对象一样,在本地方法中不能直接访问jarray对象,而是使用JNIEnv指针指向的一些方法来是用。




访问Java原始类型数组:


1)获取数组的长度:


代码4:


JNIEXPORT jint JNICALL Java_IntArray_sumArray


(JNIEnv *env, jobject obj, jintArray arr)


{



int i, sum = 0;


jsize len = (*env)->GetArrayLength(env, arr);


如代码4所示,这里获取数组的长度和普通的c语言中的获取数组长度不一样,这里使用JNIEvn的一个函数GetArrayLength。




2)获取一个指向数组元素的指针:


代码4:


jint *body = (*env)->GetIntArrayElements(env, arr, 0);


使用GetIntArrayElements方法获取指向arr数组元素的指针,注意该函数的参数,第一个是JNIEnv,第二个是数组,第三个是数组里面第三个是数组里面开始的元素




3)使用指针取出Array中的元素




代码5:


for (i=0; i<len; i++) {



sum += body[i];


}


这里使用就和普通的c中的数组使用没有什么不同了


4)释放数组元素的引用


代码6:


(*env)->ReleaseIntArrayElements(env, arr, body, 0);


和操作String中的释放String的引用是一样的,提醒JVM回收arr数组元素的引用。


这里举的例子是使用int数组的,同样还有boolean、float等对应的数组。


获取数组元素指针的对应关系:


函数            数组类型


GetBooleanArrayElements   boolean


GetByteArrayElements    byte


GetCharArrayElements    char


GetShortArrayElements    short


GetIntArrayElements     int


GetLongArrayElements    long


GetFloatArrayElements    float


GetDoubleArrayElements   double




释放数组元素指针的对应关系:


Function            Array Type


ReleaseBooleanArrayElements   boolean


ReleaseByteArrayElements    byte


ReleaseCharArrayElements    char


ReleaseShortArrayElements    short


ReleaseIntArrayElements     int


ReleaseLongArrayElements    long


ReleaseFloatArrayElements    float


ReleaseDoubleArrayElements   double




访问自定义Java对象数组




The JNI provides a separate set of functions to access elements of object arrays. You can use these functions to get and set individual object array elements.




Note: You cannot get all the object array elements at on



ce.




GetObjectArrayElement returns the object element at a given index.




SetObjectArrayElement updates the object element at a given index.




3) 访问Java对象的方法:




在本地方法中调用Java对象的方法的步骤:




①.获取你需要访问的Java对象的类:




jclass cls = (*env)->GetObjectClass(env, obj);




使用GetObjectClass方法获取obj对应的jclass。




②.获取MethodID:




jmethodID mid = (*env)->GetMethodID(env, cls, “callback”, “(I)V”);




使用GetMethdoID方法获取你要使用的方法的MethdoID。其参数的意义:




env??>JNIEnv




cls??>第一步获取的jclass




“callback”??>要调用的方法名




“(I)V”??>方法的Signature




③.调用方法:




(*env)->CallVoidMethod(env, obj, mid, depth);




使用CallVoidMethod方法调用方法。参数的意义:




env??>JNIEnv




obj??>通过本地方法穿过来的jobject




mid??>要调用的MethodID(即第二步获得的MethodID)




depth??>方法需要的参数(对应方法的需求,添加相应的参数)




注:这里使用的是CallVoidMethod方法调用,因为没有返回值,如果有返回值的话使用对应的方法,在后面会提到。




方法的Signature




方法的Signature是由方法的参数和返回值的类型共同构成的,下面是他们的结构:




“(argument-types)return-type”




其中Java程序中参数类型和其对应的值如下:




Signature  Java中的类型


Z       boolean


B       byte


C       char


S       short


I       int


J       long


F       float


D       double


L fully-qualified-class;   fully-qualified-class




[ type  type[]




( arg-types ) ret-type  method type


一个Java类的方法的Signature可以通过javap命令获取:


javap -s -p Java类名


给调用的函数传参数:


通常我们直接在methodID后面将要传的参数添加在后面,但是还有其他的方法也可以传参数:


CallVoidMethodV可以获取一个数量可变的列表作为参数;


CallVoidMethodA可以获取一个union。


调用静态方法:


就是将第二步和第三步调用的方法改为对应的:


GetStaticMethodID获取对应的静态方法的ID


CallStaticIntMethod调用静态方法


调用超类的方法:


用的比较少,自己看啦。^_^。


4)访问Java对象的属性:


访问Java对象的属性和访问Java对象的方法基本上一样,只需要将函数里面的Method改为Field即可