基本数据类型转换
在 Java 中传递的参数类型是 int,而在 JNI 中就成了 jint,这就涉及到 Java 到 JNI 的数据类型转换。
Java 类型 | Native 类型 | 字节长度 |
---|---|---|
boolean | jboolean | 8位 |
byte | jbyte | 8位 |
char | jchar | 16位 |
short | jshort | 16位 |
int | jnit | 32位 |
long | jlong | 64位 |
float | jfloat | 32位 |
double | jdouble | 64位 |
引用数据类型转换
除了基本数据类型之外,引用数据类型也有着一一对应。
Java 类型 | Native 类型 |
---|---|
objects | jobject |
java.lang.Class | jclass |
java.lang.String | jstring |
Object[] | jobjectArray |
boolean[] | jbooleanArray |
byte[] | jbyteArray |
char[] | jcharArray |
short[] | jshortArray |
int[] | jintArray |
long[] | jlongArray |
float[] | jfloatArray |
double[] | jdoubleArray |
java.lang.Throwable | jthrowable |
除了 Java 中基本数据类型的数组、Class、String 和 Throwable 外,其余所有 Java 对象的数据类型在 JNI 中都用 jobject 表示。
String 字符串操作
对于基本数据类型的操作,比如 boolean、int、float 等都大同小异,无非是在原来的数据类型前面加了一个 j来表示 JNI 数据类型。
而对于 String 类型,必须要使用合适的 JNI 函数来将 jstring 转变成 C/C++ 字符串。
对于下面的 Native 方法,传入一个字符串,并要求返回一个字符串。
external fun stringFromJNI(): String
layout1Binding.tv1.setOnLongClickListener {
val str = strFromJNI("我是Java向C传递的参数")
layout1Binding.tv1.text = str
true
}
生成的对应的 C++ 代码如下:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_znzy_myapplication_MainActivity_strFromJNI(JNIEnv *env, jobject thiz, jstring string) {
// 生成jstring
jstring returnValue = env->NewStringUTF("Hello Best");
// 把jstring类型的字符串转换为C风格的字符串,会额外申请内存
const char *str = env->GetStringUTFChars(string,0);
// 做检查判断
if (str == NULL){
return NULL;
}
// 实际操作
printf("%s",str);
LOGE("%s",str);
//释放申请的内存
env->ReleaseStringUTFChars(string,str);
return returnValue;
}
通过Java调用验证发现日志正常输出,页面正常显示
Java 层的字符串到了 JNI 就成了 jstring 类型的,但 jstring 指向的是 JVM 内部的一个字符串,它不是 C 风格的字符串 char*
,所以不能像使用 C 风格字符串一样来使用 jstring 。
JNI 支持将 jstring 转换成 UTF 编码和 Unicode 编码两种。因为 Java 默认使用 Unicode 编码,而 C/C++ 默认使用 UTF 编码。
将 jstring 转换成 UTF 编码的字符串
- GetStringUTFChars(jstring string, jboolean* isCopy)
其中,jstring 类型参数就是我们需要转换的字符串,而 isCopy 参数的值为 JNI_TRUE
或者 JNI_FALSE
,代表是否返回 JVM 源字符串的一份拷贝。如果为JNI_TRUE
则返回拷贝,并且要为产生的字符串拷贝分配内存空间;如果为JNI_FALSE
就直接返回了JVM
源字符串的指针,意味着可以通过指针修改源字符串的内容,但这就违反了 Java 中字符串不能修改的规定,在实际开发中,直接填 NULL
就好了。
当调用完GetStringUTFChars
方法时别忘了做完全检查。因为 JVM 需要为产生的新字符串分配内存空间,如果分配失败就会返回 NULL,并且会抛出 OutOfMemoryError 异常,所以要对 GetStringUTFChars 结果进行判断。
当使用完 UTF 编码的字符串时,还不能忘了释放所申请的内存空间。调用 ReleaseStringUTFChars
方法进行释放。
除了将 jstring 转换为 C 风格字符串,JNI 还提供了将 C 风格字符串转换为 jstring 类型。
通过 NewStringUTF
函数可以将 UTF 编码的 C 风格字符串转换为 jstring 类型,通过NewString
函数可以将 Unicode 编码的 C 风格字符串转换为 jstring 类型。这个 jstring 类型会自动转换成 Java 支持的 Unicode 编码格式。
除了 jstring 和 C 风格字符串的相互转换之外,JNI 还提供了其他的函数。
String 字符串函数操作总结
JNI 函数 | 描述 |
---|---|
GetStringChars / ReleaseStringChars | 获得或释放一个指向 Unicode 编码的字符串的指针(指 C/C++ 字符串) |
GetStringUTFChars / ReleaseStringUTFChars | 获得或释放一个指向 UTF-8 编码的字符串的指针(指 C/C++ 字符串) |
GetStringLength | 返回 Unicode 编码的字符串的长度 |
getStringUTFLength | 返回 UTF-8 编码的字符串的长度 |
NewString | 将 Unicode 编码的 C/C++ 字符串转换为 Java 字符串 |
NewStringUTF | 将 UTF-8 编码的 C/C++ 字符串转换为 Java 字符串 |
GetStringCritical / ReleaseStringCritical | 获得或释放一个指向字符串内容的指针(指 Java 字符串) |
GetStringRegion | 获取或者设置 Unicode 编码的字符串的指定范围的内容 |
GetStringUTFRegion | 获取或者设置 UTF-8 编码的字符串的指定范围的内容 |