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數組