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

人间观察

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

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

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

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

数据类型对应

基本数据类型

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

Java类型Native 类型Description
booleanjbooleanunsigned 8 bits
bytejbytesigned 8 bits
charjcharunsigned 16 bits
shortjshortsigned 16 bits
intjintsigned 32 bits
longjlongsigned 64 bits
floatjfloat32 bits
doublejdouble64 bits
voidvoidnot 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类型类型描述符/签名
intI
longJ
byteB
shortS
charC
floatF
doubleD
booleanZ
voidV
数组[
二维数组[[
其他引用类型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,慢慢的就能写出来了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值