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, "
", "(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数组