JNI数据类型转换
经典⽼套流程,学语⾔东西,先学它最基础的数据类型,JNI也是如此。JNI 定义了⼀系列基本数据类型和引⽤数据类型与java想对应。
1、基本数据类型
下⾯⼀张表是描述了 Java 基本数据类型和JNI中基本数据类型的相对应关系已经占⽤空间⼤⼩。
随便观察就能发现,其实就基本数据类型⽽已,JNI基本数据类型只是⽐Java基本数据类型前多了个’j’⽽已
当然,JNI中还有个Java中没有的jsize,定义如下:
typedef jint jsize;
其实jsize整型是⽤来描述基本指标和⼤⼩,没有什么神秘的。
2、引⽤数据类型
JNI 引⽤类型 与 Java 的对应关系 如下 树层次图:
跟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对象的引⽤,就会⽤到类型之间的转换:
  1)java⽅法⾥⾯将参数传⼊本地⽅法;
  2)在本地⽅法⾥⾯创建java对象;
  3)在本地⽅法⾥⾯return结果给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
unicode文件格式  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即可

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。