JNI调用2——jni.h文件中关于类型声明和env函数表指针

1.Java数据类型和C数据类型对应关系

JNI调用2——jni.h文件中关于类型声明和env函数表指针_第1张图片
JNI调用2——jni.h文件中关于类型声明和env函数表指针_第2张图片

这些对应类型定义在hotspot/src/share/vm/prims/jni.h:

/* jni_md.h contains the machine-dependent typedefs for jbyte, jint
   and jlong */

#include "jni_md.h"

typedef unsigned char   jboolean;
typedef unsigned short  jchar;
typedef short           jshort;
typedef float           jfloat;
typedef double          jdouble;

typedef jint            jsize;

#ifdef __cplusplus

class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};

typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;

#else

struct _jobject;

typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;

#endif

2.env函数表指针

JNI函数都使用到了env函数指针,该指针是每个本地方法的第一个参数。env指针指向函数表。必须在每个JNI调用前加上(*env)->,以便解析对函数指针的引用。


JNI调用2——jni.h文件中关于类型声明和env函数表指针_第3张图片

在hotspot/src/share/vm/prims/jni.h:

2.1 C语言

C语言env的类型定义为JNINativeInterface_结构体指针:

#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif

JNINativeInterface_定义如下,是一个函数指针表:

struct JNINativeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;

    void *reserved3;
    jint (JNICALL *GetVersion)(JNIEnv *env);

    jclass (JNICALL *DefineClass)
      (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
       jsize len);
    jclass (JNICALL *FindClass)
      (JNIEnv *env, const char *name);

2.2 C++

/*
 * We use inlined functions for C++ so that programmers can write:
 *
 *    env->FindClass("java/lang/String")
 *
 * in C++ rather than:
 *
 *    (*env)->FindClass(env, "java/lang/String")
 *
 * in C.
 */

struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;
#ifdef __cplusplus

    jint GetVersion() {
        return functions->GetVersion(this);
    }
    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
                       jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    }
    jclass FindClass(const char *name) {
        return functions->FindClass(this, name);
    }

C++加一个JNINativeInterface_ *functions,去掉了env参数,直接由functions来进行调用,本质跟c语言还是一样,都是通过调用函数表JNINativeInterface_。

3.jni.h中关于本地方法访问Java中对象和类的域

  • jclass (JNICALL *GetObjectClass) (JNIEnv *env, jobject obj);
    获取对象所属的类,object对隐式this参数对象的引用

  • jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
    返回类中一个域的标识符

  • jdouble (JNICALL *GetDoubleField) (JNIEnv *env, jobject obj, jfieldID fieldID);
    返回域的值

  • void (JNICALL *SetDoubleField) (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val);
    设置域的值

  • jclass (JNICALL *FindClass) (JNIEnv *env, const char *name);
    以字符串形式来指定类名

  • jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);

  • jint (JNICALL *GetStaticIntField) (JNIEnv *env, jclass clazz, jfieldID fieldID);

4.jni.h中关于本地方法调用Java方法

4.1 调用实例方法

  • class_PrintWriter = (*env)->GetObjectClass(env, out);
    获取隐式参数this的类
  • id_print = (*env)->GetMethodID(env, class_PrintWriter, "print", "(Ljava/lang/String;)V");
    获取方法ID
  • (*env)->CallVoidMethod(env, out, id_print, str);
    根据返回类型,可以使用不同的CallxxxMethod,该方法从C中调用任何Java方法。

4.2 调用静态放方法

  • jclass class_System = (*env)->FindClass(env, "java/lang/System");
    获取指定的类
  • jmethodID id_getProperty = (*env)->GetStaticMethodID(env, class_System, "getProperty",
    "(Ljava/lang/String;)Ljava/lang/String;");
    根据名称和描述符获取方法ID
  • jobject obj_ret = (env)->CallStaticObjectMethod(env, class_System, id_getProperty,(env)->NewStringUTF(env, "java.class.path"));
    根据类、方法ID和入参调用静态方法

4.3 调用构造器

  • jclass class_String = (*env)->FindClass(env, "java/lang/String");
  • jmethodID id_String = (*env)->GetMethodID(env, class_String, "", "(Ljava/lang/String;)V");
    通过指定方法名“”,并指定构造器的方法签名,获得放方法ID
  • jobject obj_String = (*env)->NewObject(env, class_String, id_String, para);
    调用构造器方法构造String对象

4.4 调用指定方法

CallNonvirtualxxxMethod,其中类对象必须为隐式参数的超类,调用指定类中指定版本方法,不使用常规的动态调用机制。

应该对应的是invokespecial -> super等

4.5 所有方法的后缀A和V版本

A版本,用于接收数组参数
V版本,用于接收va_list可变参数

5.jni.h关于jmethodId jfiledId 与反射API中Method Filed对象相互转换

  • jmethodID (JNICALL *FromReflectedMethod) (JNIEnv *env, jobject method);

  • jfieldID (JNICALL *FromReflectedField) (JNIEnv *env, jobject field);

  • jobject (JNICALL *ToReflectedMethod) (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);

  • jobject (JNICALL *ToReflectedField) (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);

6.数组的处理

  • GetArrayLength
    返回数组长度
  • GetObjectArrayElement
    返回数组元素值
  • SetObjectArrayElement
    设置新值
  • GetxxxArrayElements
    生成一个指向Java数组元素的C指针。域类型必须是基本类型。指针不再使用时,必须ReleasexxxArrayElements
  • ReleasexxxArrayElements
    通知JVM GetxxxArrayElements获得的指针不再需要
  • GetxxxArrayRegion
    将Java数组元素复制到C数组,域类型必须是基本类型
  • SetxxxArrayRegion
    将C数组元素复制到Java数组

你可能感兴趣的:(JNI调用2——jni.h文件中关于类型声明和env函数表指针)