NDK(三):JNIEnv解析
文章目录
- 一、概述
- 二、JNIEnv结构体
- 三、JNINativeInterface结构体
- 3.1 Class操作
- 3.2 反射操作
- 3.3 对象字段 & 方法操作
- 3.4 类的静态字段 & 静态方法操作
- 3.5 字符串操作
- 3.6 锁操作
- 3.7 数组操作
- 3.8 注册和反注册native方法
- 3.9 异常Exception操作
- 3.10 引用的操作
- 3.11 其它
- 四、小结
一、概述
JNIEnv(Java Native Interface Environment) 是一个JNI接口指针 (每个线程独有一个 JNIEnv 指针),指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地的方法通过JNI函数来访问JVM中的数据结构,详情如下图:
关联文章:
- NDK(一):NDK的集成
- NDK(二):JNI的数据结构
- NDK(三):JNIEnv解析
- NDK(四):Native与Java层互调
- NDK(五):JNI静态注册与动态注册
参考文章:
- JNI Functions 官方文档
二、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对象转换为jmethodIDjmethodID (*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类名、字段名称、字段类型来获取jfieldIDjfieldID (*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类名、方法名称、方法签名信息来获取jmethodIDjmethodID (*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操作
- 引用的操作