JNI 数据类型.md
JNI 数据类型
Java有基本数据类型和引用数据类型。
基本数据类型
基本数据类型的装换大概就是Java的八大数据类型+void。
Java类型 | 本地C类型 | 实际表示的C类型(win32) | Signature | 说明 |
---|---|---|---|---|
boolean | jboolean | unsigned char | Z | 无符号,8 位 |
byte | jbyte | signed char | B | 有符号,8 位 |
char | jchar | unsigned short | C | 无符号,16 位 |
short | jshort | short | S | 有符号,16 位 |
int | jint | long | I | 有符号,32 位 |
long | jlong | __int64 | J | 有符号,64 位 |
float | jfloat | float | F | 32 位 |
double | jdouble | double | D | 64 位 |
void | void | N/A | V | N/A |
Signature是Java的方法签名。
引用数据类型
Java | Native | Signature |
---|---|---|
object | jobject | L+classname +; |
Class | jclass | Ljava/lang/Class; |
String | jstring | Ljava/lang/String; |
Throwable | jthrowable | Ljava/lang/Throwable; |
Object[] | jobjectArray | [L+classname +; |
byte[] | jbyteArray | [B |
char[] | jcharArray | [C |
double[] | jdoubleArray | [D |
float[] | jfloatArray | [F |
int[] | jintArray | [I |
short[] | jshortArray | [S |
long[] | jlongArray | [J |
boolean[] | jbooleanArray | [Z |
由上表我们知道,数组的JNI层数据类型都以”Array”结尾,签名格式最前面都是“[”。有些数据类型的签名以“;”结尾,需要特别的注意。除此之外引用数据类型还存在继承关系,如下图。
由图可知,jclass、jstring、jarray、jthrowable都继承jobject,而jobjectArray、jbyteArray、jcharArray等引用数据类型都继承jarray。具体情况可以自行阅读JNI源码。
方法签名
在上面的表格中,我们除了列举数据类型,同时还列举了相对应的签名格式(Signature),方法签名就是有签名格式所组成的,方法签名在动态代理提到过。
方法签名的格式是:(参数签名格式...)返回值签名格式
。比如方法void sayHello(String content);
,它的方法签名就是(Ljava/lang/String;)V
。
想要记住签名格式并写出正确的方法签名其实有些麻烦,好在JDK为我们提供了生成方法签名的工具javap
,使用步骤:
- 使用javac编译
A.java
为A.class
,例如javac A.java
。 - 使用javap命令,比如
javap -s -p A.class
。最终结果会在CMD/SHELL窗口显示。- 选项
s
表示内部类型签名。 p
表示打印出所有的方法和成员(默认打印public成员)。
- 选项
Java与Native的数据类型装换
java传入的String参数转换为c的char*。
java传入的String参数,在c文件中被jni转换为jstring的数据类型,在c文件中声明char* test,然后test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);
注意:test使用完后,通知虚拟机平台相关代码无需再访问:(*env)->ReleaseStringUTFChars(env, jstring, test);
c中获取的一个char*的buffer传递给java
这个char*
如果是一般的字符串的话,作为string
传回去就可以了。如果是含有’\0’
的buffer
,最好作为bytearray
传出,因为可以制定copy
的length
,如果copy
到string
,可能到’\0’
就截断了。
有两种方式传递得到的数据:
- 一种是在jni中直接new一个byte数组,然后调用函数
(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);
将buffer的值copy到bytearray中,函数直接return bytearray就可以了。 - 一种是return错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下。