在jni中,数据类型和java中的数据类型是不同的,在了解java和jni类型之间的关系之后,我们才能更好的在java和native之间传递数据。
数据类型的分类
基本类型
java
jni
C/C++
大小
boolean
jboolean
uint8_t
无符号8位
byte
jbyte
int8_t
有符号8位
char
jchar
uint16_t
无符号16位
short
jshort
int16_t
有符号16位
int
jint
int32_t
有符号32位
long
jlong
int64_t
有符号64位
float
jfloat
float
32位
double
jdouble
double
64位
引用类型
java
jni
C
C++
java.lang.Class
jclass
jobject
_jclass*
java.lang.Throwable
jthrowable
jobject
_jthrowable*
java.lang.String
jstring
jobject
_jstring *
Other objects
jobject
void *
_jobject*
java.lang.Object[]
jobjectArray
jarray
_jobjectArray*
boolean[]
jbooleanArray
jarray
_jbooleanArray*
byte[]
jbyteArray
jarray
_jbyteArray*
char[]
jcharArray
jarray
_jcharArray*
short[]
jshortArray
jarray
_jshortArray*
int[]
jintArray
jarray
_jintArray*
long[]
jlongArray
jarray
_jlongArray*
float[]
jfloatArray
jarray
_jfloatArray*
double[]
jdoubleArray
jarray
_jdoubleArray*
Other arrays
Jarray
jarray
jarray*
对于引用类型,我们知道,在java中,所有类的父类都是java.long.Object,由于C语言中没有类的概念,所以在C语言中,使用void *代替,这是一个万能类型,在C++中定义了一个空的类 class _jobject {};来代替java中的类。
基本类型在jni.h中的定义:
1 2 3 4 5 6 7 8 9 10 11 12 typedef uint8_t jboolean; typedef int8_t jbyte; typedef uint16_t jchar; typedef int16_t jshort; typedef int32_t jint; typedef int64_t jlong; typedef float jfloat; typedef double jdouble; typedef jint jsize;
以下是使用C语言时jni.h中定义的数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 typedef void * jobject;typedef jobject jclass;typedef jobject jstring;typedef jobject jarray;typedef jarray jobjectArray;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 jobject jthrowable;typedef jobject jweak;
以下是使用C++时jni.h定义的数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class _jobject {};class _jclass : public _jobject {};class _jstring : public _jobject {};class _jarray : public _jobject {};class _jobjectArray : public _jarray {};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 _jthrowable : public _jobject {};typedef _jobject* jobject;typedef _jclass* jclass;typedef _jstring* jstring;typedef _jarray* jarray;typedef _jobjectArray* jobjectArray;typedef _jbooleanArray* jbooleanArray;typedef _jbyteArray* jbyteArray;typedef _jcharArray* jcharArray;typedef _jshortArray* jshortArray;typedef _jintArray* jintArray;typedef _jlongArray* jlongArray;typedef _jfloatArray* jfloatArray;typedef _jdoubleArray* jdoubleArray;typedef _jthrowable* jthrowable;typedef _jobject* jweak;
typedef 是为类型定义别名,从上面的代码来看,除了基本类型,在引用类型中,C最终是可以看成是void *,
而C++可以看成_jobject*。
全部写成void* 或者_jobject* 也是可以的,这里这么写主要是为了可读性和可扩展性。
字符集
1 2 3 在讲解数据类型的操作函数之前,先看一下字符集,为什么要讲解字符集,回到前面仔细观察一下基本类型的char,这个类型,在java中 和C/C++中占用的空间是不同的。熟悉C的朋友都知道,C语言中的char类型占用的是一个字节,而在java中char是占用了两个字节的, 而jchar也是占用了两个字节。这是因为java和C/C++使用的字符集不同,java使用的是Unicode字符集,而C使用的是ASCII字符集。
Unicode字符集
1 2 3 4 Unicode是一个全球性的字符编码标准,它为世界上几乎所有的字符(包括各种文字、符号、标点符号等)都分配了唯一的代码点。每个代码 点用十六进制表示,例如U+0041代表拉丁字母"A",U+4E2D代表汉字"中"。Unicode字符集的目标是为了包含地球上所有的书写系统。 Unicode有不同的编码方案,最常见的是UTF-8、UTF-16和UTF-32。这些编码方案允许将Unicode代码点转换为字节序列以便存储和传输。
ASCII字符集
1 2 3 4 5 6 7 8 9 10 11 12 13 ASCII字符集仅包含128个字符,包括英文字母、数字和一些特殊符号。这个字符集不足以表示全球范围内的所有字符,因此对于多语言支持 和Unicode字符,C语言需要借助宽字符类型(wchar_t)和相关的函数。 所以,总的来说,Unicode字符集是一个包含全球字符的标准,可以表示各种语言和符号。而C中的char类型在默认情况下使用的是较小的字 符集(通常是ASCII或其扩展),需要通过宽字符类型和函数来支持Unicode字符。要在C中完全支持Unicode字符集,推荐使用wchar_t和 相关的宽字符函数。 由于上面的不同,所以在使用jchar的时候要格外注意。如果java中的字符串仅包含了ASCII的字符,可以直接将jchar转换成char使用,这 是因为ASCII字符在Unicode中的表示和ASCII字符集中的表示是一致的。 但是,如果java中的字符串包含了非ASCII的字符,比如汉字(一个字节无法表示),则需要注意字符编码的转换,一般这种情况可以使用java 中的String类的getBytes方法将字符串转化为字节数组,指定为UTF-8字符集,然后在C中直接使用对应的字符集函数将字节数组转换成char 数组。
所以,接下来我们看一下常用的几类数据类型的操作。
常用数据操作函数
基本数据类型操作
字符转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 extern "C" JNIEXPORT void JNICALL Java_com_jiangc_example1_MainActivity_charTest (JNIEnv *env, jobject thiz, jchar a, jchar b) { int codePoint = (int ) b; char utf8Char[4 ]; if (codePoint < 0x80 ) { utf8Char[0 ] = (char ) codePoint; utf8Char[1 ] = '\0' ; } else if (codePoint < 0x800 ) { utf8Char[0 ] = (char )(0xC0 | (codePoint >> 6 )); utf8Char[1 ] = (char )(0x80 | (codePoint & 0x3F )); utf8Char[2 ] = '\0' ; }else { utf8Char[0 ] = (char )(0xE0 | (codePoint >> 12 )); utf8Char[1 ] = (char )(0x80 | ((codePoint >> 6 ) & 0x3F )); utf8Char[2 ] = (char )(0x80 | (codePoint & 0x3F )); utf8Char[3 ] = '\0' ; } LOGE("jchar ============================== a = %c b = %s\n" , a, utf8Char); }
字符串创建
1 2 3 4 5 6 7 8 9 10 11 12 jstring str = env->NewStringUTF("hello world" ); if (NULL == str){ }
获取java端的字符串并转化成C/C++端字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const char *cString = env->GetStringUTFChars(str, NULL );if (NULL == cString) { LOGE("获取C字符串失败\n" ); return env->NewStringUTF("error" ); } env->ReleaseStringUTFChars(str, cString);
函数的使用看上面的代码注释就可以了,需要注意的就是,在不使用的情况下,要对字符串进行释放,而且不要尝试修改获取到的字符串。
获取字符串的长度
1 2 3 4 5 6 jsize GetStringLength (jstring string ) ; int length = env->GetStringLength(str);
数组操作
注意:对于数组,我们只讲一种类型,因为其他类型操作方式一样,大家可以自己尝试使用一下其他类型的数组函数
复制数组到native
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void GetIntArrayRegion (jintArray array , jsize start, jsize len, jint* buf) ; int *pArray = nullptr; pArray = static_cast<int *>(malloc (length * sizeof (int ))); env->GetIntArrayRegion(array , 0 , length, pArray);
这样,就可以对数组进行操作了,但是,操作的修改不会被同步到java中,因为是复制的。
如果想将修改同步到java端,需要进行提交操作
数组设置(将native中的数组数据同步到java)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void SetIntArrayRegion (jintArray array , jsize start, jsize len, const jint* buf) ; pArray[3 ] = 9 ; LOGE("after pArray[3] = %d\n" , pArray[3 ]); env->SetIntArrayRegion(array , 0 , length, pArray);
注意: 一般来讲,我们不会大量使用复制数组,然后将改变的数据再同步到java,这样效率太低,一般使用指针的方式进行操作。
获取java端数组指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 jint* GetIntArrayElements (jintArray array , jboolean* isCopy) ; jint *elements = env->GetIntArrayElements(array , nullptr); if (nullptr == elements) { return nullptr; } for (int i = 0 ; i < length; ++i) { elements[i] = i + 1 ; } env->ReleaseIntArrayElements(javaArray, elements, 0 );
注意: 和string一样,凡是获取了指针的,一律记得释放,否则内存泄漏。