Android-JNI开发系列《七》补充jni与java的数据类型的对应关系和数据类型描述符

人间观察

人只有不为生存而烦恼的时候,才会追求真正想要的东西。

在前面的几篇文章中有涉及到Java和JNI的通信,比如异常回调,Java和JNI的互相调用等。其中都免不了在通信过程中需要知道Java基本数据类型,引用类型和JNI的对应关系以及基本数据类型,引用类型的类型描述符,才能够通信和使用。

这个是很重要的,是基础,有必要单独来记录下。

在 JNI 开发中,Java 的数据类型并不是直接在 JNI 里使用的,例如 int 就在JNI中是使用 jint 来表示的。

数据类型对应

基本数据类型

Java与Native映射关系如下表所示:

Java类型 Native 类型 Description
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void not applicable

引用数据类型

外面的为JNI中的,括号中的Java中的。

  • jobject
    • jclass (java.lang.Class objects)
    • jstring (java.lang.String objects)
    • jarray (arrays)
      • jobjectArray (object arrays)
      • jbooleanArray (boolean arrays)
      • jbyteArray (byte arrays)
      • jcharArray (char arrays)
      • jshortArray (short arrays)
      • jintArray (int arrays)
      • jlongArray (long arrays)
      • jfloatArray (float arrays)
      • jdoubleArray (double arrays)
  • jthrowable (java.lang.Throwable objects)

上面的层次中的jni的引用类型代表了继承关系,jbooleanArray继承jarray,jarray继承jobject,最终都继承jobject。

示例

java 层
public native void data(byte b, char c, boolean bool, short s, int i, float f, double d, long l, float[] floats);

jni层
extern "C"
JNIEXPORT void JNICALL
Java_com_bj_gxz_jniapp_data_JNIData_data(JNIEnv *env, jobject thiz, jbyte b, jchar c,
                                         jboolean j_bool,
                                         jshort s, jint i, jfloat f, jdouble d, jlong l,
                                         jfloatArray floats) {
    LOG_D("byte=%d", b);
    LOG_D("jchar=%c", c);
    LOG_D("jboolean=%d", j_bool);
    LOG_D("jshort=%d", s);
    LOG_D("jint=%d", i);
    LOG_D("jfloat=%f", f);
    LOG_D("jdouble=%lf", d);
    LOG_D("jlong=%lld", l);

    jfloat *float_p = env->GetFloatArrayElements(floats, nullptr);
    jsize size = env->GetArrayLength(floats);
    for (int index = 0; index < size; index++) {
        LOG_D("floats[%d]=%lf", index, *(float_p++));
    }
    env->ReleaseFloatArrayElements(floats, float_p, 0);
}

输出日志

2020-10-30 15:00:24.819 2588-2588/com.bj.gxz.jniapp D/JNI: byte=100
2020-10-30 15:00:24.819 2588-2588/com.bj.gxz.jniapp D/JNI: jchar=A
2020-10-30 15:00:24.819 2588-2588/com.bj.gxz.jniapp D/JNI: jboolean=1
2020-10-30 15:00:24.819 2588-2588/com.bj.gxz.jniapp D/JNI: jshort=100
2020-10-30 15:00:24.819 2588-2588/com.bj.gxz.jniapp D/JNI: jint=100
2020-10-30 15:00:24.820 2588-2588/com.bj.gxz.jniapp D/JNI: jfloat=100.000000
2020-10-30 15:00:24.820 2588-2588/com.bj.gxz.jniapp D/JNI: jdouble=100.000000
2020-10-30 15:00:24.820 2588-2588/com.bj.gxz.jniapp D/JNI: jlong=100
2020-10-30 15:00:24.820 2588-2588/com.bj.gxz.jniapp D/JNI: floats[0]=1.000000
2020-10-30 15:00:24.820 2588-2588/com.bj.gxz.jniapp D/JNI: floats[1]=2.100000
2020-10-30 15:00:24.820 2588-2588/com.bj.gxz.jniapp D/JNI: floats[2]=3.300000

关于java复杂对象的传递可以参考上篇文章。

描述符/签名

我们平时定义的int,float,String等类型在JVM虚拟机中,存储数据类型的名称时是使用描述符来存储,它们有固定的规则/语法。如下表格:

Java类型 类型描述符/签名
int I
long J
byte B
short S
char C
float F
double D
boolean Z
void V
数组 [
二维数组 [[
其他引用类型 L+类全名+;

####如何查看描述符/签名

可以使用jdk提供的javap -s A.class 命令,-s输出内部类型签名。A.class为class的全路径。试下javap -s AppInfo.class AppInfo.classAppInfo.java编译后的.class文。可以获取任何一个类的成员变量和方法的描述符/签名。

demo:
方法或者成员变量下面的descriptor就是对应它的描述符/签名。

B000000073160:methodfield guxiuzhong$ pwd
/Users/guxiuzhong/Desktop/JNIAPP/app/build/intermediates/javac/debug/classes/com/bj/gxz/jniapp/methodfield
B000000073160:methodfield guxiuzhong$ javap -s  AppInfo.class
Compiled from "AppInfo.java"

public class com.bj.gxz.jniapp.methodfield.AppInfo implements java.io.Serializable {
  public int versionCode;
    descriptor: I
  public long size;
    descriptor: J
  public com.bj.gxz.jniapp.methodfield.AppInfo(java.lang.String);
    descriptor: (Ljava/lang/String;)V

  public com.bj.gxz.jniapp.methodfield.AppInfo(java.lang.String, int);
    descriptor: (Ljava/lang/String;I)V

  public java.lang.String getVersionName();
    descriptor: ()Ljava/lang/String;

  public void setVersionName(java.lang.String);
    descriptor: (Ljava/lang/String;)V

  public int getVersionCode();
    descriptor: ()I

  public void setVersionCode(int);
    descriptor: (I)V

  public void setSize(long);
    descriptor: (J)V

  public long getSize();
    descriptor: ()J

  public java.lang.String toString();
    descriptor: ()Ljava/lang/String;
}
B000000073160:methodfield guxiuzhong$

####类描述符

类描述符的规则是:类全名(包名+类名)将原来的.分隔符换成/ 分隔符。比如

在java代码中的java.lang.String类的类描述符就是java/lang/String

java.io.InputStream类的类描述符就是java/io/InputStream

demo中的AppInfo就是com/bj/gxz/jniapp/methodfield/AppInfo

在jni中获取java的class就是通过类描述符获取,比如:

 jclass cls = env->FindClass("com/bj/gxz/jniapp/methodfield/AppInfo");

####成员变量描述符/成员变量签名

  • 基本数据类型的描述符

    基本数据类型就是如表格所示,与上面我们用javap出来的一样。

  • 引用类型的描述符

    当引用类型作为成员变量时,它的规则就是: L类全名; 大写L 英文; 类全名(包名+类名)将原来的.分隔符换成/ 分隔符

比如

在java代码中的java.lang.String类的类描述符就是Ljava/lang/String;

java.io.InputStream类的类描述符就是Ljava/io/InputStream;

demo中的AppInfo就是Lcom/bj/gxz/jniapp/methodfield/AppInfo;

####方法描述符/方法签名

规则为: (参数的域描述符的叠加)返回类型描述符,没有返回值的用V(void类型)

比如
java方法void method (int a) 对应的方法签名是 (I)V

java方法int method (byte[ ] bytes) 对应的方法签名是 ([B)I

再比如demo中的

public void setSize(long);
    descriptor: (J)V
    
public void setVersionName(java.lang.String);
    descriptor: (Ljava/lang/String;)V  
    

这玩意要记吗?熟还能生巧,不用死记,最开始可以用javap -s A.class,慢慢的就能写出来了。

猜你喜欢

转载自blog.csdn.net/ta893115871/article/details/109386087
今日推荐