Android Studio NDK 入门教程(3)--Java与C++之间的类型签名

来源:互联网 发布:网络黑白txt下载 编辑:程序博客网 时间:2024/05/17 16:15

概述

本文主要介绍Java与C++通信时函数的签名关系

方法签名

我们可以在生成的头文件中看到每个方法上面都有如下注释:

/* * Class:     com_example_wastrel_hellojni_HelloJNI * Method:    getFormCString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_wastrel_hellojni_HelloJNI_getFormCString  (JNIEnv *, jclass);

其中前两个注释很容易理解,第三个就是方法签名,为什么会有方法签名这种东西呢?

这是因为Java这边支持函数重载,即虽然参数不一样,但是方法名一样,那么在JNI层它们的方法名都会是一样的,那JNI也会犯迷糊了,得找哪个呢?
不过也正是因为其参数类型是不一样的,所以就出现了方法签名,利用方法签名和方法名来唯一确定一个JNI函数的调用。既然方法签名是基于参数类型的不同而形成的,首先要知道Java各数据类型对应的签名是什么,也就是所谓的类型签名。

类型签名转换表

jni.h里面定义了这样一套规则:

typedef union jvalue {    jboolean    z;    jbyte       b;    jchar       c;    jshort      s;    jint        i;    jlong       j;    jfloat      f;    jdouble     d;    jobject     l;} jvalue;

那么对应在JAVA中的关系如下表:

Java 类型 类型签名 boolean Z byte B char C short S int I long J float F double D void V 类 L全限定名; 数组 [类型签名

注意:上面的类后面是有一个分号的。

如何计算一个方法的类型签名

Java函数的类型签名是根据函数的参数类型以及返回类型所决定的,参数类型放在括号里面返回类型紧跟在括号外。

    //native测试方法声明    public static native String getFormCString();    //三个重载方法    public static native int TypeTest(boolean a,short b,long c,char d);    public static native Objects TypeTest(Date date,byte []m,HelloJNI k);    public static native Long Test(Character k,Double m);    //执行生成头文件命令    /*    * Class:     com_example_wastrel_hellojni_HelloJNI    * Method:    getFormCString    * Signature: ()Ljava/lang/String;    */    JNIEXPORT jstring JNICALL Java_com_example_wastrel_hellojni_HelloJNI_getFormCString    (JNIEnv *, jclass);    /*    * Class:     com_example_wastrel_hellojni_HelloJNI    * Method:    TypeTest    * Signature: (ZSJC)I    */    JNIEXPORT jint JNICALL Java_com_example_wastrel_hellojni_HelloJNI_TypeTest__ZSJC    (JNIEnv *, jclass, jboolean, jshort, jlong, jchar);    /*    * Class:     com_example_wastrel_hellojni_HelloJNI    * Method:    TypeTest    * Signature: (Ljava/util/Date;[BLcom/example/wastrel/hellojni/HelloJNI;)Ljava/util/Objects;    */    JNIEXPORT jobject JNICALL Java_com_example_wastrel_hellojni_HelloJNI_TypeTest__Ljava_util_Date_2_3BLcom_example_wastrel_hellojni_HelloJNI_2    (JNIEnv *, jclass, jobject, jbyteArray, jobject);    /*    * Class:     com_example_wastrel_hellojni_HelloJNI    * Method:    Test    * Signature: (Ljava/lang/Character;Ljava/lang/Double;)Ljava/lang/Long;    */    JNIEXPORT jobject JNICALL Java_com_example_wastrel_hellojni_HelloJNI_Test    (JNIEnv *, jclass, jobject, jobject);

通过以上代码可以可以看出,重载的方法生成的头文件后函数名后面都追加参数的类型签名,并且‘/’被替换成了下划线‘_’,‘;’被替换成了数字‘2’;‘[’被替换成了数字‘3’。在签名中()里面表示方法的参数,紧接着的是返回类型对应的签名。可以看到String getFromCString因为没有参数,所以()里面什么都没有,但其有一个返回参数String,String并不是Java的基础类型,String的全称是java.lang.String,根据签名规则就是Ljava/lang/String;,因此该方法的签名是()Ljava/lang/String。这里需要注意,如果一个Java函数没有返回即void,那么对应的返回类型签名就是V。

其余函数有兴趣可以根据上述规则推理,我相信各位程序员的推理能力,这里就不一一讲解。

后记

方法签名是为了便于Java与native代码的相互访问,后面通过C++调用Java代码的时候,方法签名尤为重要。在传递复杂类型的时候相对较为麻烦,在实际使用中并不建议传递复杂对象。这里说一下Java方法重载,在编写native函数的时候不建议使用重载,重载的方法名称很长,辨识度也不高。更建议在java中预处理重载的调用。例如:

public  class NativeFunc{    public String Func(String str){        return native_func1(str);    }    public String Func(int i){        return native_func2(i);    }    public native String native_func1(String str);    public native String native_func2(int i);}
2 0
原创粉丝点击