JNI数据类型

如果需要使用Jni中定义的标准数据类型需要将jni.h include到源码中。
JNI 提供的数据类型包括两类一类是基本数据类型,一类是引用数据类型:

基本数据类型总共有八种如下所示:

引用类型就比较多了整个继承关系如下所示:

还有一个是与访问类对象成员以及类对象方法有关的jfieldID以及jmethodID,这两个方法将会在介绍类成员调用的部分进行介绍。

关于八种基本数据类型我们先不再这里进行介绍了,我们重点介绍下字符串相关的类型以及数组相关类型调用的方法:

字符串相关方法:

jstring 对应的是Java语言中的strings类型,它和标准C语言下的字符串类型是不一样的,C语言中的字符串使用的是char *,是一个指向字符串的一个指针。所以我们需要在native层代码中使用字符串之前将jstring转换为C语言类型的字符串。JNI 提供了Unicode和UTF-8字符串到C语言字符类型之间的相互转换。GetStringUTFChars 这个方法可以将jstring类型的字符串转换为char * 类型,但是这里需要注意的是我们在调用这个方法进行转换的时候不要忘了对返回值进行判空处理,因为Java虚拟机需要分配内存用于存储UTF字符串。这有可能导致内存分配失败。还需要注意的是我们在使用完这些字符后需要调用ReleaseStringUTFChars来释放之前申请的内存。如果没有及时调用ReleaseStringUTFChars或者调用失败,那么就会导致内存泄漏,这个是很严重的问题。
接下来我们还需要看下GetStringUTFChars的第三个参数:这个是用于表示当前访问的字符串是来自原始字符串的拷贝还是直接指向原始字符串,如果返回的为JNI_TRUE那么表示返回的字符串是来自原始字符串的拷贝,如果返回的是JNI_FALSE那么返回的字符串是直接指向原来的字符串,这种情况下要注意不能去修改返回值,否则原始的字符串内容也将会同时被修改。一般我们在不需要关心Java虚拟机返回的是拷贝还是指向原始位置的指针的时候我们可以传递NULL;

如果我们需要使用将C语言类型的字符串转换为Java类型的字符串,那么需要使用NewStringUTF,如果这个方法中分配内存失败那么将会抛出OutOfMemoryError异常,并且返回NULL。

JNIEXPORT jstring JNICALL
Java_com_idealist_myapplication_MainActivity_getLine(JNIEnv *env, jobject instance, jstring line_) {

jboolean isCopy = JNI_FALSE;
const char *line = (*env)->GetStringUTFChars(env, line_, &isCopy);
if(line == NULL) {
return NULL;
}
char buf[256];
stpcpy(buf," This is a String add by xiaohai ");
strcat(buf,line);
(*env)->ReleaseStringUTFChars(env, line_, line);
return (*env)->NewStringUTF(env, buf);
}

下面是字符串相关的JNI方法的说明:

方法名 说明
GetStringChars/ReleaseStringChars 获取或者释放一个指向Unicode格式字符串的内容的指针,这种情况下有可能返回字符串的拷贝
GetStringUTFChars/ReleaseStringUTFChars 获取或者释放一个指向UTF格式字符串的内容的指针,这种情况下有可能返回字符串的拷贝
GetStringLength 返回在Unicode string中的字符串个数
GetStringUTFLength 返回在UTF-8 string中的字符串个数
NewString 通过传入的Unicode C string 创建一个java.lang.String对象
NewStringUTF 通过传入的UTF-8 C string 创建一个java.lang.String对象
Get/ReleaseStringCritical 这两个方法一般成对使用,在这两个方法之间的native代码必须不能调用哪些会导致当前线程阻塞或者等待其他运行在Java虚拟机上的线程,这些限制可以避免Java虚拟机在native代码持有一个通过GetStringCritical返回的直接指针的时候进行垃圾回收
GetStringUTFRegion/SetStringRegion 由于GetStringUTFRegion没有进行内存分配所以我们不需要进行out-of-memory条件的判断,并且不需要对资源进行释放
GetStringUTFRegion/SetStringRegion 这个与上面的类似

下面是关于如何选择对应方法的说明:

数组相关方法:

使用GetIntArrayRegion方法来访问:

JNIEXPORT jint JNICALL
Java_com_idealist_myapplication_MainActivity_sumOfTheArrayByRegion(JNIEnv *env, jobject instance,
jintArray intArray_, jint size) {
jint buf[size];
jint i, sum = 0;
(*env)->GetIntArrayRegion(env, intArray_, 0, size, buf);
for (i = 0; i < 10; i++) {
sum += buf[i];
}
return sum;
}

第三个参数表示起始的index,第四个参数表示需要copy的元素的个数,一旦这些元素都被拷贝到C缓存器那么我们就可以在native层使用这些参数了。同样我们可以使用SetIntArrayRegion方法来设置某个数组的值。对于小型,大小固定的数组Get/SetArrayRegion是最有效的方式,因为在C堆栈中分配内存是十分快速的方式。

使用GetIntArrayElements方法来访问

Java_com_idealist_myapplication_MainActivity_sumOfTheArray(JNIEnv *env, jobject instance,
jintArray intArray_, jint size) {
jint *intArray = (*env)->GetIntArrayElements(env, intArray_, NULL);
if(intArray == NULL) {
return 0;
}
jint sum = 0;
jint index =0;
for(index = 0;index<size;index++) {
sum += intArray[index];
}
(*env)->ReleaseIntArrayElements(env, intArray_, intArray, 0);
return sum;
}

GetArrayLength 返回数组中的元素长度
NewArray 创建一个指定长度的数组

Contents
  1. 1. JNI数据类型
  2. 2. 字符串相关方法:
  3. 3. 数组相关方法: