Java -- JNI中的数据类型
JNI,即Java Native Interface,意为Java本地调用。JNI是一种Java技术,通过使用JNI,我们不仅可以在Java代码里调用C/C++实现的native方法,也可以在native代码里面调用Java实现的方法。从这里大概也可以得出,JNI的出现,一个重要的原因就是“避免重复造轮子“,提高代码复用性。Android framework中,JNI的使用频率非常高;Java层实现业务逻辑、流程控制,再通过JNI调用native方法来完成具体的业务实现。
在Android平台上,JNI就是一座将Native世界和Java世界的天堑变成通途的桥梁。Android平台上JNI所处的位置: Java世界 <--> JNI层 <--> Native世界(引用)。
在JNI中,Java层面的数据类型和引用类型都是不能直接被解析的。即使向JNI传递的是一个简单的int类型,它也不能被直接识别,必须进行相应的类型转换,转换成JNI中与之对应的类型。
下面主要介绍Java和JNI中的类型映射关系。
Java中存在的基本数据类型,在JNI中都有一种特殊的类型与之对应:
Java类型 | native类型 | 类型描述(C/C++) |
byte | jbyte | 有符号的8位整型 |
char | jchar | 无符号的16位整型 |
short | jshort | 带符号的16位整型 |
int | jint | 带符号的32位整型 |
long | jlong | 带符号的64位整型 |
float | jfloat | 32位浮点型 |
double | jdouble | 64位浮点型 |
boolean | jboolean | 无符号的8位整型 |
这些基本数据类型都是可以在native层直接使用的 。不过,务必注意转化成native类型后对应数据类型的字长,例如jchar在native语言中是16位,占两个字节,这和普通的char占一个字节是完全不一样的。其次,Java中void的native类型依旧是void。
类似的,Java中的引用数据类型,在JNI中也有一种特殊的类型与之对应:
Java类型 | native类型 | 类型描述 |
java.lang.Object | jobject | 任何Java对象,或者没有对应Java类型的对象 |
java.lang.String | jstring | 字符串对象 |
java.lang.Class | jclass | Class对象 |
java.lang.Throwable | jthrowable | Throwable对象 |
Object[] | jobjectArray | 任意类型的对象数组 |
byte[] | jbyteArray | byte类型数组 |
char[] | jcharArray | char类型数组 |
short[] | jshortArray | short类型数组 |
int[] | jintArray | int类型数组 |
long[] | jlongArray | long类型数组 |
float[] | jfloatArray | float类型数组 |
double[] | jdoubleArray | double类型数组 |
boolean[] | jbooleanArray | 布尔类型数组 |
我们要通过Java层声明的native类型方法,例如:
public native static boolean startDhcp(String interfaceName);
通过JNI调用到native实现(C/C++),必须要有一种联系,将这两类方法连接起来。考虑到Java支持函数重载,JNI选择函数的签名信息(参数列表、返回值)作为连接Java native类型方法和native实现方法的桥梁。有了函数名和它的签名信息,就可以很方便的找到所需的函数了。同样地,在JNI函数签名信息中,也存在对应的类型映射关系:
Java类型 | 类型签名 |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
类 | L + 全限定名,String的类型签名是"Ljava/lang/String;"包括最后的分号 |
数组 | [ + 类型签名,byte[]的类型签名是"'[B";"[java/lang/Object;"的Java类型是Object[] |
函数声明 | "(参数1,参数2,...)返回值类型" |
请注意,如果Java类型是数组,则标识中会有一个“[”,另外,引用类型(除基本类型的数组外)的标识最后都有一个“;”。
下面从Android源码中,列举一些JNI函数签名的示例:
Java函数声明 | JNi函数签名 |
int resetConnections(String interfaceName, int mask) | "(Ljava/lang/String;I)I" |
boolean startDhcp(String interfaceName) | "(Ljava/lang/String;)Z" |
boolean getDhcpResults(String interfaceName, DhcpResults dhcpResults) | "(Ljava/lang/String;Landroid/net/DhcpResults;)Z" |
int bindSocketToNetwork(int socketfd, int netId) | "(II)I" |
String getDhcpError() | "()Ljava/lang/String;" |
函数的返回值类型出现在函数签名参数列表括号的右侧。
最后JNI中还定义了一个jvalue类型:
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
这个union类型经常作为JNI中参数数组的元素类型:
static void android_animation_PropertyValuesHolder_callMultipleFloatMethod(
JNIEnv* env, jclass pvhObject, jobject target, jlong methodID, jfloatArray arg)
{
jsize parameterCount = env->GetArrayLength(arg);
jfloat *floatValues = env->GetFloatArrayElements(arg, NULL);
jvalue* values = new jvalue[parameterCount];
for (int i = 0; i < parameterCount; i++) {
values[i].f = floatValues[i];
}
env->CallVoidMethodA(target, reinterpret_cast<jmethodID>(methodID), values);
delete[] values;
env->ReleaseFloatArrayElements(arg, floatValues, JNI_ABORT);
}