02 jni基本变量与转换

 

 

  • 基本类型的 java 与 c 对照图
Java 类型本地 C 类型实际表示的 C 类型
(Win32)
说明
booleanjbooleanunsigned char无符号,8 位
bytejbytesigned char有符号,8 位
charjcharunsigned short无符号,16 位
shortjshortshort有符号,16 位
intjintlong有符号,32 位
longjlong__int64有符号,64 位
floatjfloatfloat32 位
doublejdoubledouble64 位
voidvoidN/AN/A

在jni.h 和 jni_md.h中声明

typedef unsigned char jboolean;

typedef unsigned short jchar;

typedef short jshort;

typedef float jfloat;

typedef double jdouble;

typedef long jint;

typedef __int64 jlong;

typedef signed char jbyte;

 

  • Java对象类型

 

Java对象在C\C++代码中的形式如下:

class _jclass : public _jobject {};

class _jthrowable : public _jobject {};

class _jstring : public _jobject {};

class _jarray : public _jobject {};

class _jbooleanArray : public _jarray {};

class _jbyteArray : public _jarray {};

class _jcharArray : public _jarray {};

class _jshortArray : public _jarray {};

class _jintArray : public _jarray {};

class _jlongArray : public _jarray {};

class _jfloatArray : public _jarray {};

class _jdoubleArray : public _jarray {};

class _jobjectArray : public _jarray {};

 

  • Java数组在本地代码中的处理

 

我们可以使用GetFieldID获取一个Java数组变量的ID,然后用GetObjectFiled取得该数组变量到本地方法,返回值为jobject,然后我们可以强制转换为j<Type>Array类型。

 

typedef jarray jbooleanArray;

typedef jarray jbyteArray;

typedef jarray jcharArray;

typedef jarray jshortArray;

typedef jarray jintArray;

typedef jarray jlongArray;

typedef jarray jfloatArray;

typedef jarray jdoubleArray;

typedef jarray jobjectArray;

 

j<Type>Array类型是JNI定义的一个对象类型,它并不是C/C++的数组,如int[]数组,double[]数组等等。所以我们要把j<Type>Array类型转换为C/C++中的数组来操作。

 

JNIEnv定义了一系列的方法来把一个j<Type>Array类型转换为C/C++数组或把C/C++数组转换为j<Type>Array。

 

// 获取&释放primitive数组

void * GetPrimitiveArrayCritical(jarray array, jboolean*isCopy)

void ReleasePrimitiveArrayCritical(jarray array, void*carray, jint mode)

 

上面是JNIEnv提供给本地代码调用的数组操作函数,大致可以分为下面几类:

 

1) 获取数组的长度

 

jsize GetArrayLength(jarray array);

 

2) 对象类型数组的操作

 

jsize GetArrayLength(jarray array) // 获得数组的长度

jobjectArray NewObjectArray(jsize len, jclass clazz, jobjectinit) // 创建对象数组,指定其大小

jobject GetObjectArrayElement(jobjectArray array, jsizeindex) // 获得数组的指定元素

void SetObjectArrayElement(jobjectArray array, jsizeindex,jobject val) // 设置数组元素

 

JNI没有提供直接把Java的对象类型数组(Object[ ])直接转到C++中的jobject[ ]数组的函数。而是直接通过Get/SetObjectArrayElement这样的函数来对Java的Object[ ]数组进行操作

 

3) 对基本数据类型数组的操作

 

基本数据类型数组的操作方法比较多,大致可以分为如下几类:

// 创建数组

jbooleanArray NewBooleanArray(jsize len) // 创建Boolean数组,指定其大小

// 获得指定类型数组的元素

jboolean * GetBooleanArrayElements(jbooleanArray array,jboolean *isCopy)

// 释放指定数组

void ReleaseBooleanArrayElements(jbooleanArrayarray,jboolean *elems,jint mode)

// 获取某区域的数据

void GetBooleanArrayRegion(jbooleanArray array,jsizestart, jsize len, jboolean *buf)

// 设置某区域的数据

void SetBooleanArrayRegion(jbooleanArray array, jsizestart, jsize len,const jboolean *buf)

 

这类函数可以把Java基本类型的数组转换到C/C++中的数组。有两种处理方式,一是拷贝一份传回本地代码,另一种是把指向Java数组的指针直接传回到本地代码,处理完本地化的数组后,通过Realease<Type>ArrayElements来释放数组。处理方式由Get方法的第二个参数isCopied来决定。

Realease<Type>ArrayElements(<Type>Arrayarr,<Type>* array, jint mode)用这个函数可以选择将如何处理Java和C/C++本地数组:

其第三个参数mode可以取下面的值:

l 0:对Java的数组进行更新并释放C/C++的数组

l JNI_COMMIT:对Java的数组进行更新但是不释放C/C++的数组

l JNI_ABORT:对Java的数组不进行更新,释放C/C++的数组

 

 

  • 局部引用与全局引用

 

1) JNI中的引用变量

 

Java代码与本地代码里在进行参数传递与返回值复制的时候,要注意数据类型的匹配。对于int, char等基本类型直接进行拷贝即可,对于Java中的对象类型,通过传递引用实现。VM保证所有的Java对象正确的传递给了本地代码,并且维持这些引用,因此这些对象不会被Java的gc(垃圾收集器)回收。因此,本地代码必须有一种方式来通知VM本地代码不再使用这些Java对象,让gc来回收这些对象。

 

JNI将传递给本地代码的对象分为两种:局部引用和全局引用。

 

l 局部引用:只在上层Java调用本地代码的函数内有效,当本地方法返回时,局部引用自动回收。

 

l 全局引用:只有显示通知VM时,全局引用才会被回收,否则一直有效,Java的gc不会释放该引用的对象。

 

默认的话,传递给本地代码的引用是局部引用。所有的JNI函数的返回值都是局部引用。

 

 

2) 手动释放局部引用情况

 

虽然局部引用会在本地代码执行之后自动释放,但是有下列情况时,要手动释放:

 

l 本地代码访问一个很大的Java对象时,在使用完该对象后,本地代码要去执行比较复杂耗时的运算时,由于本地代码还没有返回,Java收集器无法释放该本地引用的对象,这时,应该手动释放掉该引用对象。

 

(*env)->DeleteLocalRef(env, elemArr);

 

这个情形的实质,就是允许程序在native方法执行期间,java的垃圾回收机制有机会回收native代码不在访问的对象。

 

l 创建的工具函数,它会被未知的代码调用,在工具函数里使用完的引用要及时释放。

 

l 不返回的本地函数。例如,一个可能进入无限事件分发的循环中的方法。此时在循环中释放局部引用,是至关重要的,这样才能不会无限期地累积,进而导致内存泄露。

 

局部引用只在创建它们的线程里有效,本地代码不能将局部引用在多线程间传递。一个线程想要调用另一个线程创建的局部引用是不被允许的。将一个局部引用保存到全局变量中,然后在其它线程中使用它,这是一种错误的编程。

 

3) 全局引用

 

在一个本地方法被多次调用时,可以使用一个全局引用跨越它们。一个全局引用可以跨越多个线程,并且在被程序员手动释放之前,一直有效。和局部引用一样,全局引用保证了所引用的对象不会被垃圾回收。

 

JNI允许程序员通过局部引用来创建全局引用, 全局引用只能由NewGlobalRef函数创建。下面是一个使用全局引用例子:

stringClass = (*env)->NewGlobalRef(env, localRefCls);

(*env)->DeleteLocalRef(env, localRefCls);

 

4) 释放全局引用

 

在native代码不再需要访问一个全局引用的时候,应该调用DeleteGlobalRef来释放它。如果调用这个函数失败,Java VM将不会回收对应的对象。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值