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

Posted Wastrel_xyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Studio NDK 入门教程--Java与C++之间的类型签名相关的知识,希望对你有一定的参考价值。

概述

本文主要介绍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 类型类型签名
booleanZ
byteB
charC
shortS
intI
longJ
floatF
doubleD
voidV
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);

以上是关于Android Studio NDK 入门教程--Java与C++之间的类型签名的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio NDK 入门教程--优雅的在C++中输出Logcat

Android Studio NDK 入门教程--JNI动态注册本地方法

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

Android Studio NDK 入门教程--Java对象的传递与修改

NDK开发 从入门到放弃(七:Android Studio 2.2 CMAKE 高效NDK开发)

Android Studio NDK 入门教程--JNI签名验证防止恶意调用