• JNI 的学习(二)对于 JNI 数据类型、方法名和方法签名的一些认识


    JNI 的学习(二)对于 JNI 数据类型、方法名和方法签名的一些认识

      我们知道,利用 javah 生成的 c/c++ 头文件的时候,会对 java 中定义的 native 函数生成对应的jni层函数,如下:

     1 #ifndef _Included_com_clay_example_JNITest
     2 #define _Included_com_clay_example_JNITest
     3 #ifdef __cplusplus
     4 extern "C" {
     5 #endif
     6 /*
     7  * Class:     com_clay_example_JNITest
     8  * Method:    getJNIString
     9  * Signature: ()Ljava/lang/String;
    10  */
    11 JNIEXPORT jstring JNICALL Java_com_clay_example_JNITest_getJNIString
    12   (JNIEnv *, jobject);

      我们可以看到方法名是以 Java_com_clay_example_JNITest_ 等开头的,还有什么所谓的 Signature,那这些其实都是什么意思呢,今天我们就来简单地认识一下。

    一、JNI 数据类型

      我们知道 Java 的数据类型是跟 C/C++ 的数据类型是不一样的,而 JNI 是处于 Java 和 Native 本地库(大部分是用 C/C++ 写的)中间的一层,JNI 对于两种不同的数据类型之间必须做一种转换,所以在 JNI 跟 Java 之间就会有数据类型的对应关系。

      在 JNI 中,提供了以下各种数据类型,可以分为原生类型和引用类型:

      对于原生类型有:jchar, jbyte, jshort, jint, jlong, jfloat, jdouble, jboolean,其与 java 端的数据类型对应如下表:

      jni.h 文件中对于数据类型的定义如下:

     1 /* Primitive types that match up with Java equivalents. */
     2 typedef uint8_t  jboolean; /* unsigned 8 bits */
     3 typedef int8_t   jbyte;    /* signed 8 bits */
     4 typedef uint16_t jchar;    /* unsigned 16 bits */
     5 typedef int16_t  jshort;   /* signed 16 bits */
     6 typedef int32_t  jint;     /* signed 32 bits */
     7 typedef int64_t  jlong;    /* signed 64 bits */
     8 typedef float    jfloat;   /* 32-bit IEEE 754 */
     9 typedef double   jdouble;  /* 64-bit IEEE 754 */
    10 
    11 /* "cardinal indices and sizes" */
    12 typedef jint     jsize;

      在这里可以看到具体的数据类型,对于 jint 又重新 typedef 了一下,所以在 jni 中看到 jsize 就是看到了 jint

      Java 中 与 JNI 中的数据类型的对应关系如下:

    Java JNI
    boolean jboolean
    byte jbyte
    char jchar
    short jshort
    int jint
    long jlong
    float jfloat
    double jdouble

      对于引用类型有 jobject、jclass、jstring、jarray、jthrowable;我们看下 jni.h 中的定义:

      针对于 C/C ++ 类型的引用:一次列举出来

     1 #ifdef __cplusplus
     2 /*
     3  * Reference types, in C++
     4  */
     5 class _jobject {};
     6 class _jclass : public _jobject {};
     7 class _jstring : public _jobject {};
     8 class _jarray : public _jobject {};
     9 class _jobjectArray : public _jarray {};
    10 class _jbooleanArray : public _jarray {};
    11 class _jbyteArray : public _jarray {};
    12 class _jcharArray : public _jarray {};
    13 class _jshortArray : public _jarray {};
    14 class _jintArray : public _jarray {};
    15 class _jlongArray : public _jarray {};
    16 class _jfloatArray : public _jarray {};
    17 class _jdoubleArray : public _jarray {};
    18 class _jthrowable : public _jobject {};
    19 
    20 typedef _jobject*       jobject;
    21 typedef _jclass*        jclass;
    22 typedef _jstring*       jstring;
    23 typedef _jarray*        jarray;
    24 typedef _jobjectArray*  jobjectArray;
    25 typedef _jbooleanArray* jbooleanArray;
    26 typedef _jbyteArray*    jbyteArray;
    27 typedef _jcharArray*    jcharArray;
    28 typedef _jshortArray*   jshortArray;
    29 typedef _jintArray*     jintArray;
    30 typedef _jlongArray*    jlongArray;
    31 typedef _jfloatArray*   jfloatArray;
    32 typedef _jdoubleArray*  jdoubleArray;
    33 typedef _jthrowable*    jthrowable;
    34 typedef _jobject*       jweak;
    35 
    36 
    37 #else /* not __cplusplus */
    38 
    39 /*
    40  * Reference types, in C.
    41  */
    42 typedef void*           jobject;
    43 typedef jobject         jclass;
    44 typedef jobject         jstring;
    45 typedef jobject         jarray;
    46 typedef jarray          jobjectArray;
    47 typedef jarray          jbooleanArray;
    48 typedef jarray          jbyteArray;
    49 typedef jarray          jcharArray;
    50 typedef jarray          jshortArray;
    51 typedef jarray          jintArray;
    52 typedef jarray          jlongArray;
    53 typedef jarray          jfloatArray;
    54 typedef jarray          jdoubleArray;
    55 typedef jobject         jthrowable;
    56 typedef jobject         jweak;
    57 
    58 #endif /* not __cplusplus */

      从这里可以很清晰的看出 C 或 C ++ 中引用类型的差异,知道了不同的数据类型的转换关系,我们就知道在什么情况下,应该对数据进行怎么样的处理。

      这个不强求硬记,无论是学习还是工作,手中的事情总是繁杂的,用的多自然就记住了,用的少自然记的不太牢靠,我们只需掌握方法,记不记得住都无所谓,记得住更好,记不住现查也是很快(不过也不难记,就 5 中类型,其余的都是 array 的转换)

      我这里有张别人画的图,我这里就直接引用一下:

      还是非常清晰的展示了其中之间的关系的。

    二、JNI 命令规则

      对于传统的 JNI 编程来说,JNI 方法跟 Java 类方法的名称之间有一定的对应关系,要遵循一定的命名规则,如下:

      (1)、前缀:Java_

      (2)、类的全限定名,用下划线进行分隔(_):com_clay_example_JNITest_

      (3)、方法名:getJNIString()

      (4)、Jni 函数指定的第一个参数:JNIEnv*

      (5)、Jni 函数指定的第二个参数 jobject (八大基本数据类型,jobject、jclass、jstring、jarray、jthrowable 及衍生 array 类型)

      (6)、实际 Java 参数:jstring、jint.....

      (7)、返回值的参数:  jstring、jint......

      所以对于在 Java 类 com.clay.example.JNITest 中的一个方法:

    1 public native String getJNIString();

      其对应的 Jni 层的方法如下:

    1 JNIEXPORT jstring JNICALL Java_com_clay_example_JNITest_getJNIString
    2 (JNIEnv* env, jobject obj)

      如果不这样命名,当把动态库加载进 DVM 的时候,通过 JNIEnv * 指针去查找 Java Native 方法对应的 JNI 方法的时候,就会找不到了。注意,我们也可以利用函数注册的方法,将 Java 层的方法名跟 JNI 层的方法名的对应关系保存起来,注册到 DVM 中,就不需要这样的命名规范了。 

    三、JNI 方法签名

      为什么会有方法签名这种东西呢?这是因为 Java 这边支持函数重载,即虽然参数不一样,但是方法名一样,那么在 JNI 层它们的方法名都会是一样的,那 JNI 也会犯迷糊了,得找哪个呢?

      不过也正是因为其参数类型是不一样的,所以就出现了方法签名,利用方法签名和方法名来唯一确定一个 JNI 函数的调用。

      既然方法签名是基于参数类型的不同而形成的,首先要知道 Java 各数据类型对应的签名是什么,也就是所谓的类型签名,

      在 jni.h 文件中就已经定义了这样一套规则,如下:

     1 typedef union jvalue {
     2     jboolean    z;
     3     jbyte       b;
     4     jchar       c;
     5     jshort      s;
     6     jint        i;
     7     jlong       j;
     8     jfloat      f;
     9     jdouble     d;
    10     jobject     l;
    11 } jvalue;

      对应于 Java 端的数据类型,我们也可以看一下下面的表:

    Java 类型 类型签名
    boolean Z
    byte B
    char C
    short S
    int I
    long L
    float F
    double D
    L全限定名;比如 String,其签名为 Ljava/lang/util/String;
    数组 [类型签名,比如[B

      对于上面的类,要注意其后面还有一个分号。

      而对一个方法,其签名就是其参数类型签名和返回值类型签名的字符串,其形式如下:

        (类型签名1类型签名2...)返回值类型签名

      每个类型签名之间是没有空格的,下面看看两个例子:

      方法1:

    1 public native String getJNIString(String tail, int index);

      其对应的签名如下:

    1 (Ljava/lang/String;I)Ljava/lang/String

      方法2:

    1 public int addValue(int index, String value,int[] arr)

      其对应的签名如下:

    1 (ILjava/lang/String;[I)I

      相信通过这两个例子,大家也能够了解了方法签名是什么样的形式了吧,对于JNI这些奇形怪状的表示形式也有一定的了解了。

      (参数类型签名)返回类型签名
      (Ljava/lang/String;I)Ljava/lang/String ------ 方法参数为 String 类型 与 int 类型,返回类型 String

      long fun(int n, String str, int[] arr); 其方法签名为 (ILjava/lang/String;[I)L 括号里的内容分成三部分,之间没有空格,即 ”I”, ”Ljava/lang/String;” 和 ”[I” ,分别代表 int, String, int[]。括号后面是返回值类型签名,L 代表 long 型。

  • 相关阅读:
    ADO.NET Entity Framework(5)esql (二)。
    google首页动态图片代码
    ms sql 聚合事例
    GridView 一些操作
    《狼与狗的故事》
    Asp.net日期字符串格式化显示方法
    解决网爬工具爬取页面信息出现乱码的问题
    esql的查询结果集 ObjectQuery
    去空空格 null sql
    不安装 oracle的客户,就可以使用pl/sql访问远程oracle 数据库的方法
  • 原文地址:https://www.cnblogs.com/Reverse-xiaoyu/p/14119794.html
Copyright © 2020-2023  润新知