JNI調用2——jni.h文件中關于類型聲明和env函數表指針

1.Java數據類型和C數據類型對應關系

這些對應類型定義在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)->,以便解析對函數指針的引用。



在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, "<init>", "(Ljava/lang/String;)V");
    通過指定方法名“<init>”,并指定構造器的方法簽名,獲得放方法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數組
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容