文章目录
一、概述
JNIEnv(Java Native Interface Environment) 是一个JNI接口指针 (每个线程独有一个 JNIEnv 指针),指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地的方法通过JNI函数来访问JVM中的数据结构,详情如下图:
关联文章:
参考文章:
二、JNIEnv结构体
我们知道 JNI 方法一般都是使用 JNIEnv 去调用,而 JNIEnv 又是一个指针,所以JNI中有哪些函数,只需要找到 JNIEnv 的实现体就可以了。
struct _JNIEnv;
# C中直接使用JNINativeInterface指针进行操作。
typedef const struct JNINativeInterface* C_JNIEnv;
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
从上述代码可以看到,C中直接使用 JNINativeInterface 指针进行操作。在C++文件中是对_JNIEnv 起的一个别名 JNIEnv。
下面我们来看下 _JNIEnv 结构体的定义。
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint GetVersion()
{ return functions->GetVersion(this); }
jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
// ...略
}
通过上面的代码可知,_JNIEnv 内部所有的操作都是委托给JNINativeInterface指针进行操作的,相当于_JNIEnv只是一个代理层。而在C语言中直接使用的是JNINativeInterface指针,这就是JNIEnv在C和C++调用方式不一致的原因。
三、JNINativeInterface结构体
下面我们来分析一下 JNINativeInterface 的结构体,JNINativeInterface 结构体中主要包含如下几类的操作:
- Class操作
- 反射操作
- 对象字段 & 方法操作
- 类的静态字段 & 静态方法操作
- 字符串操作
- 锁操作
- 数组操作
- 注册和反注册native方法
- 异常Exception操作
- 引用的操作
下文中 JNINativeInterface 内的方法有时会省略一些参数信息,我们可以通过 JNI Functions 官方文档 来查看函数原型。
以 FindClass 函数为例:
struct JNINativeInterface {
jclass (*FindClass)(JNIEnv*, const char*);
}
// 函数原型为:
jclass FindClass(JNIEnv *env, const char *name);
详情如下图所示:
3.1 Class操作
struct JNINativeInterface {
/*获取当前JNI版本信息:*/
jint (*GetVersion)(JNIEnv *);
// 定义一个类:类是从某个字节数组buf中读取出来的
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize);
// 查找全限定名为name的类:如String类:"java/lang/String"
jclass (*FindClass)(JNIEnv*, const char*);
// 获取当前类的父类:通常在使用FindClass获取到类之后,再调用这个函数
jclass (*GetSuperclass)(JNIEnv*, jclass);
}
3.2 反射操作
struct JNINativeInterface {
// 将一个Method对象转换为jmethodID
jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
jfieldID (*FromReflectedField)(JNIEnv*, jobject);
// 通过jmethodID,反射得到Method对象
jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
}
3.3 对象字段 & 方法操作
struct JNINativeInterface {
// 通过指定jclass类名、字段名称、字段类型来获取jfieldID
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
// ---------- 操作Field -------------
// 通过类的jobject和jfieldID获取字段的jobject对象。
void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
// 8种基本类型字段的获取与赋值。
void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);
// ---------- 操作Method -------------
// 通过指定jclass类名、方法名称、方法签名信息来获取jmethodID
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
// 8种基本类型:boolean、byte、char、short、int、long、float、double。
// ...是可变长度的参数,参数类型相同。
jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
// va_list是可变长度的参数,参数类型可以不同。
jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
// jvalue 是8中基本类型
jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
}
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
小结:
- 方法参数通过支持3种不同的参数类型来覆盖所有的参数场景。
- 单个参数场景:使用 jvalue 来表示支持一个参数的场景。
- 多个相同参数场景:使用 … 来表示支持同类型的多个参数。
- 多个不同参数场景:使用 va_list 来表示支持不同类型的多个参数。
3.4 类的静态字段 & 静态方法操作
struct JNINativeInterface {
// ---------- 操作 Static Field -------------
jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*, const char*);
void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
// ---------- 操作 Static Method -------------
// 与GetMethodID方法类似。
jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
// 三种参数方式:...、va_list、jvalue。
jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
}
3.5 字符串操作
struct JNINativeInterface {
jstring (*NewString)(JNIEnv*, const jchar*, jsize);
jsize (*GetStringLength)(JNIEnv*, jstring);
const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
jstring (*NewStringUTF)(JNIEnv*, const char*);
jsize (*GetStringUTFLength)(JNIEnv*, jstring);
/* JNI spec says this returns const jbyte*, but that's inconsistent */
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
}
3.6 锁操作
struct JNINativeInterface {
jint (*MonitorEnter)(JNIEnv*, jobject);
jint (*MonitorExit)(JNIEnv*, jobject);
}
3.7 数组操作
struct JNINativeInterface {
jbyteArray (*NewByteArray)(JNIEnv*, jsize);
jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray, jbyte*, jint);
void (*SetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*);
void (*GetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, jbyte*);
}
3.8 注册和反注册native方法
struct JNINativeInterface {
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);
jint (*UnregisterNatives)(JNIEnv*, jclass);
}
动态注册JNI代码时会使用 RegisterNatives 函数。具体请参考 - NDK(五):JNI静态注册与动态注册
3.9 异常Exception操作
struct JNINativeInterface {
jint (*Throw)(JNIEnv*, jthrowable);
jint (*ThrowNew)(JNIEnv *, jclass, const char *);
jthrowable (*ExceptionOccurred)(JNIEnv*);
void (*ExceptionDescribe)(JNIEnv*);
void (*ExceptionClear)(JNIEnv*);
void (*FatalError)(JNIEnv*, const char*);
}
3.10 引用的操作
struct JNINativeInterface {
// 全局变量的创建与删除
jobject (*NewGlobalRef)(JNIEnv*, jobject);
void (*DeleteGlobalRef)(JNIEnv*, jobject);
// 局部变量的创建与删除
jobject (*NewLocalRef)(JNIEnv*, jobject);
void (*DeleteLocalRef)(JNIEnv*, jobject);
// 对象的比较
jboolean (*IsSameObject)(JNIEnv*, jobject, jobject);
}
3.11 其它
struct JNINativeInterface {
jint (*GetJavaVM)(JNIEnv*, JavaVM**);
jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
jint (*PushLocalFrame)(JNIEnv*, jint);
jobject (*PopLocalFrame)(JNIEnv*, jobject);
}
四、小结
- JNIEnv 是一个代理,实际的操作全部委托给 JNINativeInterface 指针执行。
- JNINativeInterface 结构体中主要包含如下几类的操作:
- Class操作
- 反射操作
- 对象字段 & 方法操作
- 类的静态字段 & 静态方法操作
- 字符串操作
- 锁操作
- 数组操作
- 注册和反注册native方法
- 异常Exception操作
- 引用的操作