JNI/NDK入门指南之JNI数据类型,描述符详解

       JNI/NDK入门指南之JNI数据类型,描述符详解

   通过前面的章节正确姿势了解Android中JNI/NDK,我想读者朋友们一定对JNI/NDK的基本概念印象深刻了吗,那么接下来就要开始我们的JNI编程学习了。学编程的东西,一般法则先学习它的最基础数据类型,同理JNI也是如此。既然JNI是用来C/C++和Java通信的,那么JNI为之定义了一系列基本数据类型和引用数据类型用来与Java交织呼应。那么在接下来的篇章里面我们会围绕JNI的数据类型和描述符为中心而开展。

注意:这里都是以32位CPU架构来说明!



基本数据类型

先从最简单的基本数据类型开始,看看JNI语法为其定义的相关法则。

Java Launage Type JNI Native Type C/C++ Launage Type Type Description
boolean jboolean unsigned char unsigned 8 bits
byte jbyte signed char signed 8 bits
char jchar unsigned short unsigned 16 bits
short jshort signed short signed 16 bits
int jint signed int signed 32 bits
long jlong signed long signed 64 bits
flat jflat flat 32 bits
double jdouble double 64 bits

注意:这些数据类型是可以直接在JNI中直接使用的,不需要进行转换。

	jbyte result=0xff;
	jint size;
	jbyte* timeBytes;


引用数据类型

Java Launage Type JNI Native Type Type Description
java.lang.Object jobject 可以表示任何Java的对象,或者没有
JNI对应类型的Java对象(实例方法的强制参数)
java.lang.String jstring Java的String字符串类型的对象
java.lang.Class jclass Java的Class类型对象(静态方法的强制参数)
Object[] jobjectArray Java任何对象的数组
boolean[] jbooleanArray Java boolean型数组
byte[] jbyteArray Java byte型数组
char[] jcharArray Java char型数组
short[] jshortArray Java short型数组
int[] jintArray Java int型数组
long[] jlongArray Java long型数组
float[] jfloatArray Java float型数组
double[] jdoubleArray Java double型数组
java.lang.Throwable jthrowable Java的Throwable类型,表示异常的所有类型和子类
void void N/A

注意
(1) JNI中与Java字符串String相对应的jstring也是引用类型,这个切记。
(2) 数组也是引用数据类型,引用类型不能直接使用,要经过JNI函数转换才能使用。参见如下代码:

	//获得一维数组的类引用,即jintArray类型
	jclass intArrayClass = env->FindClass("[I"); 
	int len = 10;
	//构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为len
	jobjectArray obejctIntArray  =  env->NewObjectArray(len,intArrayClass , NULL);

和Java中的继承关系类似,JNI引用类型也存在一个继承关系,当我们在进行JNI实际开发的时候,可以参照进行相对应的转换:

在这里插入图片描述



类描述符

假设这么一个场景,在JNI的Native方法中,我们要使用Java中的对象怎么办(包括你自定义的或者Android源码中已有的)?即在C/C++中怎么找到Java中的类,这就要使用到JNI开发中的类描述符了。JNI提供的函数中有个FindClass()就是用来查找Java类的,其参数必须放入一个类描述符字符串,类描述符一般是类的完整名称(包名+类名)。这么说是不是很抽象,下面我举几个栗子说明一下:

Java中String类

完整类名:   java.lang.String
对应类描述符: java/lang/String
即一个 Java 类对应的描述符,就是类的全名,其中 . 要换成 / ,最后 不要忘掉末尾的分号。
其实,在实践中,我发现可以直接用该类型的域描述符取代,也是可以成功的。
例如:  jclass intArrCls = env->FindClass(“java/lang/String”)
等同于: jclass intArrCls = env->FindClass(“Ljava/lang/String;”)

Android中Surface类

完整类名:   android.view.Surface
对应类描述符: android/view/Surface
即一个 Android 类对应的描述符,就是类的全名,其中 . 要换成 /

    int err = RegisterMethodsOrDie(env, "android/view/Surface",
            gSurfaceMethods, NELEM(gSurfaceMethods));

    jclass clazz = FindClassOrDie(env, "android/view/Surface");


域描述符

看到这个名词是不是感觉有点懵逼,啥是与描述符。让我为你一一道来,域描述符是JNI中对Java数据类型的一种表示方法(就是对Java类中的变量,在JNI世界的定义),即在JVM虚拟机中,存储数据类型的名称时,是使用指定的描述符来存储,而不是我们习惯的 int,float 等。虽然有类描述符,但是类描述符里并没有说明基本类型和引用数据类型如何表示,所以在JNI中就引入了域描述符的概念。

1 基本类型域描述符

JNI在域描述符中已经定义好了基本类型的表示,其对照表格如下:

Java Launage Type Field Description
int I
long J
byte B
short S
char C
double D
boolean Z
void V
其它引用类型 L+类全名+;
数组 [
方法 (参数)返回值

2 引用类型域描述符

一般引用类型则为 L + 该类型类描述符 + ; (注意,这儿的分号“;”只得是JNI的一部分,而不是我们汉语中的分段,下同)。这么说自我感觉也有那么点抽象,让我们举几个栗子说明一下:

2.1 String引用类型域描述符

Java类型:  java.lang.String
JNI 域描述符:Ljava/lang/String;
扩展开来就是 即一个 Java 类对应的描述符,就是 L 加上类的全名,其中 . 要换成 / ,最后 不要忘掉末尾的分号。是不是有点按图索骥的感觉,那么让我么下面接着照葫芦画瓢再接再厉。

2.2 数组引用类型域描述符

对于数组,其通用规则为为 : [ + 其元素类型的域描述符,二维数组就是两个 [ ,以此类推,n维数组就有几个[ + 其元素类型的域描述符。
Java类型:   int[]
JNI域描述符: [I
Java类型:   float[]
JNI域描述符: [F
Java类型:   String[]
JNI域描述符: [Ljava/lang/String;
Java类型:   Object[ ]
JNI域描述符: [[Ljava/lang/Object;
多维数组则是 n个[ +该元素域描述符 , N代表的是几维数组。例如:
Java类型:   int[][]
JNI域描述符: [[I
Java类型:   float[][]
JNI域描述符: [[F

	get_field(env, javaBean_clazz, "boolValue", "Z", &javaBean_t.boolValue);
	get_field(env, javaBean_clazz, "charValue", "C", &javaBean_t.charValue);
	get_field(env, javaBean_clazz, "doubleValue", "D", &javaBean_t.doubleValue);
	get_field(env, javaBean_clazz, "intValue", "I", &javaBean_t.intVaule);
	get_field(env, javaBean_clazz, "array", "[B", &javaBean_t.byteArray);
	get_field(env, javaBean_clazz, "mDoubleDimenArray", "[[I", &javaBean_t.double_dimen_array);
	get_field(env, javaBean_clazz, "stringValue", "Ljava/lang/String;", &javaBean_t.stringValue);
	get_field(env, javaBean_clazz, "mInnerClass", "Lcom/pax/object2struct/JavaBean$InnerClass;", &javaBean_t.inner_message);


方法描述符

Java世界的类,变量都在JNI世界找到了对应的规则了,各位读者亲朋友们,有没有发现Java类中的方法是不是还没有一个对应的法则呢,本章节就要来说JNI中对应Java方法的方法描述符了。方法描述符定义了方法的返回值和参数的表示形式,将参数类型的域描述符按声明顺序放入一对括号中(如果没有参数则不需要括号),括号后跟返回值类型的域描述符即形成方法描述符。我想各位对方法描述符的定义有了一定理解了,那么让我们来举几个栗子说明一下:

Java Method Method Description
String fun() ()Ljava/lang/String;
int fun(int i, Object object) (ILjava/lang/Object;)I
void fun(byte[ ] bytes) ([B)V
int fun(byte data1, byte data2) (BB)I
void fun() ()V

注意,void的处理比较特殊,如果返回值为void,那么方法描述符中必须使用V表示,当void作为参数的时候,忽略。

static JNINativeMethod gMethods[] = {
	{"getJavaBeanFromNative", "()Lcom/pax/object2struct/JavaBean;",(void*)Java_com_pax_object2struct_JniTransfer_getJavaBeanFromNative },
	{"transferJavaBeanToNative", "(Lcom/pax/object2struct/JavaBean;)V",(void*)Java_com_pax_object2struct_JniTransfer_transferJavaBeanToNative },
};	

补充点:当我们使用JDK内置工具javah生成JNI的头文件的时候(当然这个工具得配置一下才能使用),我们会发现javah头文件注释中已经生成了本地方法的方法描述符,但是它偏不叫方法描述符而是叫做Signature,所以我们也可以把方法描述符直译为签名,形如:

#include <jni.h>
/* Header for class com_pax_paxndk_PaxNative */

#ifndef _Included_com_pax_paxndk_PaxNative
#define _Included_com_pax_paxndk_PaxNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_pax_paxndk_PaxNative
 * Method:    getCurrentTime
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_pax_paxndk_PaxNative_getCurrentTime
  (JNIEnv *, jobject);
/*
 * Class:     com_pax_apifunctest_test_PaxNative
 * Method:    Prn_Vline
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_pax_apifunctest_test_PaxNative_Prn_1Vline
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif


写在最后

   通过本章的细述,我想各位读者朋友们一定对JNI的数据类型,以及描述符有了比较详细的了解了。在本篇章的最后,我们有说到了javah工具,这个我们会在后面的章节里面详细介绍的。不要着急。

发布了89 篇原创文章 · 获赞 92 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/tkwxty/article/details/103526316
今日推荐