上一篇:03-JNI 类型和数据结构
本章是 JNI 函数的参考章节。它提供了所有 JNI 函数的完整列表。它还介绍了 JNI 函数表的具体布局。
注意:使用 "必须 "一词来描述对 JNI 程序员的限制。例如,当你看到某个 JNI 函数必须接收一个非 NULL 对象时,你就有责任确保 NULL 不会传递给该 JNI 函数。因此,JNI 实现无需在该 JNI 函数中执行 NULL 指针检查。在明确不允许的情况下传递 NULL 可能会导致意外异常或致命崩溃。
定义为既可返回 NULL 又可在出错时抛出异常的函数,可以选择只返回 NULL 表示出错,但不抛出任何异常。例如,JNI 实现可能认为 "内存不足 "是暂时的,因此可能不希望抛出 OutOfMemoryError ,因为这看起来是致命的(JDK API java.lang.Error 文档:"表示严重问题,合理的应用程序不应尝试捕获")。
本章部分内容改编自 Netscape 的 JRI 文档。
4.1 接口函数表
每个函数都可以通过 JNIEnv 参数以固定偏移量访问。JNIEnv 类型是一个指向存储所有 JNI 函数指针的结构的指针。其定义如下:
typedef const struct JNINativeInterface *JNIEnv;
虚拟机将初始化函数表,如下代码示例所示。请注意,前三个条目是为将来与 COM 兼容而保留的。此外,我们还在函数表开头附近保留了一些额外的 NULL 条目,以便将来与类相关的 JNI 操作可以添加到 FindClass 之后(前),而不是表的末尾。
const struct JNINativeInterface ... = {NULL,NULL,NULL,NULL,GetVersion,DefineClass,FindClass,FromReflectedMethod,FromReflectedField,ToReflectedMethod,GetSuperclass,IsAssignableFrom,ToReflectedField,Throw,ThrowNew,ExceptionOccurred,ExceptionDescribe,ExceptionClear,FatalError,PushLocalFrame,PopLocalFrame,NewGlobalRef,DeleteGlobalRef,DeleteLocalRef,IsSameObject,NewLocalRef,EnsureLocalCapacity,AllocObject,NewObject,NewObjectV,NewObjectA,GetObjectClass,IsInstanceOf,GetMethodID,CallObjectMethod,CallObjectMethodV,CallObjectMethodA,CallBooleanMethod,CallBooleanMethodV,CallBooleanMethodA,CallByteMethod,CallByteMethodV,CallByteMethodA,CallCharMethod,CallCharMethodV,CallCharMethodA,CallShortMethod,CallShortMethodV,CallShortMethodA,CallIntMethod,CallIntMethodV,CallIntMethodA,CallLongMethod,CallLongMethodV,CallLongMethodA,CallFloatMethod,CallFloatMethodV,CallFloatMethodA,CallDoubleMethod,CallDoubleMethodV,CallDoubleMethodA,CallVoidMethod,CallVoidMethodV,CallVoidMethodA,CallNonvirtualObjectMethod,CallNonvirtualObjectMethodV,CallNonvirtualObjectMethodA,CallNonvirtualBooleanMethod,CallNonvirtualBooleanMethodV,CallNonvirtualBooleanMethodA,CallNonvirtualByteMethod,CallNonvirtualByteMethodV,CallNonvirtualByteMethodA,CallNonvirtualCharMethod,CallNonvirtualCharMethodV,CallNonvirtualCharMethodA,CallNonvirtualShortMethod,CallNonvirtualShortMethodV,CallNonvirtualShortMethodA,CallNonvirtualIntMethod,CallNonvirtualIntMethodV,CallNonvirtualIntMethodA,CallNonvirtualLongMethod,CallNonvirtualLongMethodV,CallNonvirtualLongMethodA,CallNonvirtualFloatMethod,CallNonvirtualFloatMethodV,CallNonvirtualFloatMethodA,CallNonvirtualDoubleMethod,CallNonvirtualDoubleMethodV,CallNonvirtualDoubleMethodA,CallNonvirtualVoidMethod,CallNonvirtualVoidMethodV,CallNonvirtualVoidMethodA,GetFieldID,GetObjectField,GetBooleanField,GetByteField,GetCharField,GetShortField,GetIntField,GetLongField,GetFloatField,GetDoubleField,SetObjectField,SetBooleanField,SetByteField,SetCharField,SetShortField,SetIntField,SetLongField,SetFloatField,SetDoubleField,GetStaticMethodID,CallStaticObjectMethod,CallStaticObjectMethodV,CallStaticObjectMethodA,CallStaticBooleanMethod,CallStaticBooleanMethodV,CallStaticBooleanMethodA,CallStaticByteMethod,CallStaticByteMethodV,CallStaticByteMethodA,CallStaticCharMethod,CallStaticCharMethodV,CallStaticCharMethodA,CallStaticShortMethod,CallStaticShortMethodV,CallStaticShortMethodA,CallStaticIntMethod,CallStaticIntMethodV,CallStaticIntMethodA,CallStaticLongMethod,CallStaticLongMethodV,CallStaticLongMethodA,CallStaticFloatMethod,CallStaticFloatMethodV,CallStaticFloatMethodA,CallStaticDoubleMethod,CallStaticDoubleMethodV,CallStaticDoubleMethodA,CallStaticVoidMethod,CallStaticVoidMethodV,CallStaticVoidMethodA,GetStaticFieldID,GetStaticObjectField,GetStaticBooleanField,GetStaticByteField,GetStaticCharField,GetStaticShortField,GetStaticIntField,GetStaticLongField,GetStaticFloatField,GetStaticDoubleField,SetStaticObjectField,SetStaticBooleanField,SetStaticByteField,SetStaticCharField,SetStaticShortField,SetStaticIntField,SetStaticLongField,SetStaticFloatField,SetStaticDoubleField,NewString,GetStringLength,GetStringChars,ReleaseStringChars,NewStringUTF,GetStringUTFLength,GetStringUTFChars,ReleaseStringUTFChars,GetArrayLength,NewObjectArray,GetObjectArrayElement,SetObjectArrayElement,NewBooleanArray,NewByteArray,NewCharArray,NewShortArray,NewIntArray,NewLongArray,NewFloatArray,NewDoubleArray,GetBooleanArrayElements,GetByteArrayElements,GetCharArrayElements,GetShortArrayElements,GetIntArrayElements,GetLongArrayElements,GetFloatArrayElements,GetDoubleArrayElements,ReleaseBooleanArrayElements,ReleaseByteArrayElements,ReleaseCharArrayElements,ReleaseShortArrayElements,ReleaseIntArrayElements,ReleaseLongArrayElements,ReleaseFloatArrayElements,ReleaseDoubleArrayElements,GetBooleanArrayRegion,GetByteArrayRegion,GetCharArrayRegion,GetShortArrayRegion,GetIntArrayRegion,GetLongArrayRegion,GetFloatArrayRegion,GetDoubleArrayRegion,SetBooleanArrayRegion,SetByteArrayRegion,SetCharArrayRegion,SetShortArrayRegion,SetIntArrayRegion,SetLongArrayRegion,SetFloatArrayRegion,SetDoubleArrayRegion,RegisterNatives,UnregisterNatives,MonitorEnter,MonitorExit,GetJavaVM,GetStringRegion,GetStringUTFRegion,GetPrimitiveArrayCritical,ReleasePrimitiveArrayCritical,GetStringCritical,ReleaseStringCritical,NewWeakGlobalRef,DeleteWeakGlobalRef,ExceptionCheck,NewDirectByteBuffer,GetDirectBufferAddress,GetDirectBufferCapacity,GetObjectRefType,GetModule,IsVirtualThread};
4.2 常数
JNI API 中使用了许多通用常量。
4.2.1 布尔值
#define JNI_FALSE 0
#define JNI_TRUE 1
4.2.2 返回值
JNI 函数的一般返回值常量。
#define JNI_OK 0 /* success */
#define JNI_ERR (-1) /* unknown error */
#define JNI_EDETACHED (-2) /* thread detached from the VM */
#define JNI_EVERSION (-3) /* JNI version error */
#define JNI_ENOMEM (-4) /* not enough memory */
#define JNI_EEXIST (-5) /* VM already created */
#define JNI_EINVAL (-6) /* invalid arguments */
4.3 版本信息
4.3.1 获取版本
/*** @brief JNIEnv 接口函数表中的索引号:4* @param evn JNI 接口指针,不得为 NULL 。* @return 返回高 16 位的主版本号和低 16 位的次版本号。*/
jint GetVersion(JNIEnv *env);
返回本地方法接口的版本。对于 Java SE Platform 21 及更高版本,它返回 JNI_VERSION_21 。下表列出了 Java SE 平台各版本中包含的 JNI 版本(对于旧版本的 JNI,使用的是 JDK 版本而不是 Java SE 平台):
4.3.2 版本常量
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006
#define JNI_VERSION_1_8 0x00010008
#define JNI_VERSION_9 0x00090000
#define JNI_VERSION_10 0x000a0000
#define JNI_VERSION_19 0x00130000
#define JNI_VERSION_20 0x00140000
#define JNI_VERSION_21 0x00150000
4.4 Class操作
4.4.1 DefineClass
/*** @brief JNIEnv 接口函数表中的索引: 5* * @param env JNI 接口指针,不得为 NULL 。* @param name 要定义的类或接口的名称。该字符串以修改后的 UTF-8 编码。该值可以是 NULL ,也必须与类文件数据中编码的名称一致。* @param loader 分配给已定义类的类加载器。该值可以是 NULL ,表示 "空类加载器"(或 "引导类加载器")。* @param buf 包含1 个文件数据的缓冲区。 NULL 值将导致 ClassFormatError .* @param bufLen 缓冲区长度* @return jclass 返回一个 Java 类对象,如果出现错误,则返回 NULL 。* @throw ClassFormatError 如果类别数据没有指定有效的类别。* @throw ClassCircularityError 如果一个类或接口是它自己的超类或超接口。* @throw OutOfMemoryError 如果系统内存不足。* @throw SecurityException 如果调用者试图在 "java "包树中定义一个类。*/
jclass DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize bufLen);
4.4.2 FindClass
/*** @brief JNIEnv 接口函数表中的索引 6* * @param env JNI 接口指针,不得为 NULL * @param name 全称类名(即以 " / "分隔的包名,后面是类名)。如果名称以 " [ "(数组签名字符)开头,则返回一个数组类。* 字符串以修改后的 UTF-8 编码。如果数值为 NULL ,则可能导致 NoClassDefFoundError 或崩溃。* @return jclass 根据全称返回类对象,如果找不到该类,则返回 NULL 。* @throw ClassFormatError 如果类别数据没有指定有效的类别* @throw ClassCircularityError 如果一个类或接口是它自己的超类或超接口* @throw NoClassDefFoundError 如果找不到所请求的类或接口的定义* @throw OutOfMemoryError 如果系统内存不足*/
jclass FindClass(JNIEnv *env, const char *name);
在 JDK 1.1 版中,该函数用于加载本地定义的类。它会在 CLASSPATH 环境变量指定的目录和 zip 文件中搜索指定名称的类。
自 JDK 1.2 起,Java 安全模型允许非系统类加载和调用本地方法。 FindClass 会定位与当前本地方法相关的类加载器,即声明本地方法的类的类加载器。如果本地方法属于系统类,则不会涉及类加载器。否则,将调用适当的类加载器来加载、链接和初始化被命名的类。
自 JDK 1.2 起,当通过调用接口调用 FindClass 时,当前没有本地方法或其相关的类加载器。在这种情况下,将使用 ClassLoader.getSystemClassLoader的结果 。这是虚拟机为应用程序创建的类加载器,可以定位 java.class.path 属性中列出的类。
如果从库生命周期函数钩子调用 FindClass ,类加载器的确定方法如下:
①. 为 JNI_OnLoad 和 JNI_OnLoad_L 时,使用加载本地程序库的类的类加载器;
②. 对于 JNI_OnUnload 和 JNI_OnUnload_L ,使用由 ClassLoader.getSystemClassLoader 返回的类加载器(因为加载时使用的类加载器可能已不存在)
name 参数是全称类名或数组类型特征。例如, java.lang.String 类的全称是:
"java/lang/String"
数组类 java.lang.Object[] 的数组类型签名是:
"[Ljava/lang/Object;"
4.4.3 GetSuperclass
/*** @brief JNIEnv 接口函数表中的索引 10** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @return jclass 返回 clazz 或 NULL 所代表的类的超类*/
jclass GetSuperclass(JNIEnv *env, jclass clazz);
如果 clazz 表示除类 Object 之外的任何其他类,那么该函数将返回表示由 clazz 指定的类的超类的对象。如果 clazz 表示类 Object ,或 clazz 表示接口,则此函数返回 NULL 。
4.4.4 IsAssignableFrom
/*** @brief JNIEnv 接口函数表中的索引 11** @param env JNI 接口指针,不得为 NULL* @param clazz1 第一类参数,不得为 NULL* @param clazz2 第二类参数,不得为 NULL* @return jboolean 如果以下任一条件为真,则返回 JNI_TRUE:* ①. 第一个和第二个类参数指向同一个 Java 类* ②. 第一类是第二类的子类* ③. 第一个类的接口之一是第二个类*/
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1, jclass clazz2);
确定 clazz1 的对象是否可以安全地转换为 clazz2 。
4.5 模块操作
4.5.1 GetModule
/*** @brief JNIEnv 接口函数表中的索引 233** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @return jobject 返回类或接口所属的模块* @since JDK/JRE 9*/
jobject GetModule(JNIEnv *env, jclass clazz);
返回该类所属模块的 java.lang.Module 对象。如果类不在已命名的模块中,则返回类加载器的未命名模块。如果类表示数组类型,则此函数返回元素类型的 Module 对象。如果类代表原始类型或 void ,则返回 java.base 模块的 Module 对象。
4.6 线程操作
4.6.1 IsVirtualThread
/*** @brief JNIEnv 接口函数表中的索引 234** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,可能是 NULL 值* @return jboolean 如果对象是虚拟线程,则返回 JNI_TRUE* @since JDK/JRE 21*/
jboolean IsVirtualThread(JNIEnv *env, jobject obj);
测试对象是否为虚拟线程。
4.7 异常情况
4.7.1 Throw
/*** @brief JNIEnv 接口函数表中的索引 13** @param env JNI 接口指针,不得为 NULL* @param obj 1 个对象,不得为 NULL* @return jint 成功时返回 0;失败时返回负值* @throw The java.lang.Throwable object obj*/
jint Throw(JNIEnv *env, jthrowable obj);
抛出一个 java.lang.Throwable 对象。
4.7.2 ThrowNew
/*** @brief JNIEnv 接口函数表中的索引 14** @param env JNI 接口指针,不得为 NULL* @param clazz java.lang.Throwable 的子类,一定不是 NULL* @param message 用于构建 java.lang.Throwable 对象的信息。字符串以修改后的 UTF-8 编码。该值可以是 NULL;* @return jint 成功时返回 0;失败时返回负值。* @throw 新建的 java.lang.Throwable 对象*/
jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
从指定的类中构造一个异常对象,该异常对象的信息由 message 指定,并导致该异常被抛出。
4.7.3 ExceptionOccurred
/*** @brief JNIEnv 接口函数表中的索引 15** @param env JNI 接口指针,不得为 NULL* @return jthrowable 返回当前正在抛出的异常对象,如果当前没有异常抛出,则返回 NULL*/
jthrowable ExceptionOccurred(JNIEnv *env);
确定是否有异常抛出。在本地代码调用 ExceptionClear() 或 Java 代码处理异常之前,异常不会被抛出。
4.7.4 ExceptionDescribe
/*** @brief JNIEnv 接口函数表中的索引 16* * @param env JNI 接口指针,不得为 NULL*/
void ExceptionDescribe(JNIEnv *env);
向系统错误报告通道(如 stderr )打印异常和堆栈回溯。调用该函数的副作用是清除挂起异常。这是一个用于调试的方便例程。
4.7.5 ExceptionClear
/*** @brief JNIEnv 接口函数表中的索引 17** @param env JNI 接口指针,不得为 NULL*/
void ExceptionClear(JNIEnv *env);
清除当前抛出的任何异常。如果当前没有异常抛出,则此例程没有任何作用。
4.7.6 FatalError
/*** @brief JNIEnv 接口函数表中的索引 18** @param env JNI 接口指针,不得为 NULL* @param msg 错误信息。字符串以修改后的 UTF-8 编码。可能是 NULL 值。*/
void FatalError(JNIEnv *env, const char *msg);
引发致命错误,虚拟机无法恢复。此函数不会返回。
4.7.7 ExceptionCheck
/*** @brief JNIEnv 接口函数表中的索引 228** @param env JNI 接口指针,不得为 NULL* @return jboolean 当有异常等待处理时,返回 JNI_TRUE ;否则,返回 JNI_FALSE*/
jboolean ExceptionCheck(JNIEnv *env);
我们引入了一个方便的函数来检查待处理的异常,而无需创建对异常对象的本地引用。
4.8 全局与局部引用
㈠. 全局引用
4.8.1 NewGlobalRef
/*** @brief JNIEnv 接口函数表中的索引 21** @param env JNI 接口指针,不得为 NULL* @param obj 全局或局部引用。可以是 NULL 值,在这种情况下,该函数将返回 NULL* @return jobject 返回给定 obj 的全局引用, 如果出现以下情况,可能返回 NULL* ①. obj 指向 null* ②. 系统内存耗尽* ③. obj 是一个弱全局引用,已被垃圾回收*/
jobject NewGlobalRef(JNIEnv *env, jobject obj);
4.8.2 DeleteGlobalRef
/*** @brief JNIEnv 接口函数表中的索引 22** @param env JNI 接口指针,不得为 NULL* @param globalRef 全局引用。可能是 NULL 值,在这种情况下,该函数不执行任何操作*/
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
删除 globalRef 指向的全局引用
㈡. 局部引用
本地引用在本地方法调用期间有效。本地方法返回后,它们会被自动释放。每个本地引用都会耗费一定的 Java 虚拟机资源。程序员需要确保本地方法不会过度分配本地引用。虽然本地引用会在本地方法返回 Java 后自动释放,但过度分配本地引用可能会导致本地方法执行期间虚拟机内存不足。
4.8.3 DeleteLocalRef
/*** @brief JNIEnv 接口函数表中的索引 23* * @param env JNI 接口指针,不得为 NULL* @param localRef 本地引用。如果此处传递的值为 NULL ,则函数不执行任何操作*/
void DeleteLocalRef(JNIEnv *env, jobject localRef);
删除 localRef 指向的本地引用。
注:JDK/JRE 1.1 提供了上述 DeleteLocalRef 函数,以便程序员手动删除本地引用。例如,如果本地代码遍历一个可能很大的对象数组,并在每次迭代中使用一个元素,那么在下一次迭代中创建新的本地引用之前,删除不再使用的数组元素的本地引用是一个很好的做法。从 JDK/JRE 1.2 开始,为本地引用生命周期管理提供了一组额外的函数。它们是下面列出的四个函数。
4.8.4 EnsureLocalCapacity
/*** @brief JNIEnv 接口函数表中的索引 26** @param env JNI 接口指针,不得为 NULL* @param capacity 所需本地引用的最小数量。必须大于等于 0* @return jint 成功时为 JNI_OK* @since JDK/JRE 1.2*/
jint EnsureLocalCapacity(JNIEnv *env, jint capacity);
确保在当前线程中至少可以创建给定数量的本地引用。成功时返回 0,否则返回负数并抛出 OutOfMemoryError 。
在进入本地方法之前,虚拟机会自动确保至少可以创建 16 个本地引用。
为了实现向后兼容,虚拟机分配的本地引用会超出确保的容量。(作为调试支持,虚拟机可能会向用户发出创建过多本地引用的警告)。在 JDK 中,程序员可以提供 -verbose:jni 命令行选项来打开这些信息)。如果创建的本地引用不能超过保证容量,虚拟机就会调用 FatalError 。
某些 Java 虚拟机实现可能会选择限制最大值 capacity ,这可能会导致函数返回错误(如 JNI_ERR 或 JNI_EINVAL )。例如,HotSpot JVM 实现使用 -XX:+MaxJNILocalCapacity 标志(默认值:65536)。
4.8.5 PushLocalFrame
/*** @brief JNIEnv 接口函数表中的索引 19** @param env JNI 接口指针,不得为 NULL* @param capacity 所需本地引用的最小数量。必须大于 0* @return jint 成功时为 JNI_OK* @since JDK/JRE 1.2*/
jint PushLocalFrame(JNIEnv *env, jint capacity);
创建一个新的本地参照系,其中至少可以创建给定数量的本地参照系。成功时返回 0,失败时返回负数和待定的 OutOfMemoryError 。
请注意,在以前的局部帧中创建的局部引用在当前局部帧中仍然有效。
与 EnsureLocalCapacity 一样,某些 Java 虚拟机实现可能会选择限制最大值 capacity ,这可能会导致函数返回错误。
4.8.6 PopLocalFrame
/*** @brief JNIEnv 接口函数表中的索引 20** @param env JNI 接口指针,不得为 NULL* @param result 传递给上一个本地参照系的对象,可以是 NULL* @return jobject 返回给定 result 对象在上一个局部参照系中的局部参照值,如果给定 result 对象是 NULL ,则返回 NULL* @since JDK/JRE 1.2*/
jobject PopLocalFrame(JNIEnv *env, jobject result);
弹出当前局部参照系,释放所有局部参照,并返回给定 result 对象在上一个局部参照系中的局部参照。
如果不需要返回上一帧的引用,则将 NULL 设为 result 。
4.8.7 NewLocalRef
/*** @brief JNIEnv 接口函数表中的索引 25** @param env JNI 接口指针,不得为 NULL* @param ref 对象引用,函数为其创建新的局部引用。可以是 NULL 值* @return jobject 返回一个新的本地引用,该引用指向与 ref 相同的对象。* 如果出现以下情况,可能返回 NULL:* ①. ref 指 null* ②. 系统内存耗尽* ③. 系统内存耗尽* @since JDK/JRE 1.2*/
jobject NewLocalRef(JNIEnv *env, jobject ref);
创建一个新的局部引用,该引用指向与 ref 相同的对象。给定的 ref 可以是全局引用、局部引用或 NULL 。如果 ref 指向 null ,则返回 NULL 。
4.9 全局弱引用
弱全局引用是一种特殊的全局引用。与普通全局引用不同,弱全局引用允许对底层 Java 对象进行垃圾回收。弱全局引用可用于任何使用全局或局部引用的情况。
弱全局引用与 Java 幻象引用 ( java.lang.ref.PhantomReference ) 有关。在确定对象是否为幻影可及时,指向特定对象的弱全局引用将被视为指向该对象的幻影引用(参见 java.lang.ref )。
在垃圾回收器清除指向同一对象的 PhantomReference 的同时,这种弱全局引用的功能等同于 NULL 。
由于本地方法运行时可能会进行垃圾回收,因此弱全局引用所引用的对象可以随时被释放。虽然可以在使用全局引用的地方使用弱全局引用,但一般来说不宜这样做,因为弱全局引用的功能可能会在不经意间等同于 NULL 。
IsSameObject 可以用来比较弱全局引用和非 NULL 本地或全局引用。如果对象相同,只要另一个引用没有被删除,弱全局引用就不会在功能上等同于 NULL 。
IsSameObject 也可用于将弱全局引用与 NULL 进行比较,以确定底层对象是否已被释放。不过,程序员不应该依赖这种检查来确定弱全局引用是否可以(作为非 NULL 引用)。在未来的任何 JNI 函数调用中使用,因为中间的垃圾回收可能会改变弱全局引用。
相反,建议使用 JNI 函数 NewLocalRef 或 NewGlobalRef 获取底层对象的(强)局部或全局引用。如果对象已被释放,这些函数将返回 NULL 。否则,新引用将阻止底层对象被释放。新引用(如果非 NULL )可用于访问底层对象,并在不再需要时删除。
4.9.1 NewWeakGlobalRef
/*** @brief JNIEnv 接口函数表中的索引 226** @param env JNI 接口指针,不得为 NULL* @param obj 要创建全局弱引用的对象* @return jweak 返回给定 obj 的全局弱引用* 如果出现以下情况,可能返回 NULL:* ①. obj 指 null* ②. 系统内存耗尽* ③. obj 是一个弱全局引用,已被垃圾回收* @throw OutOfMemoryError 如果系统内存耗尽* @since JDK/JRE 1.2*/
jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);
创建一个新的弱全局引用。弱全局引用不会阻止对给定对象的垃圾回收。 IsSameObject 可以用来测试引用所指向的对象是否已被释放。如果 obj 指向 null ,或 obj 是弱全局引用,或虚拟机内存耗尽,则返回 NULL 。如果虚拟机内存耗尽,将抛出 OutOfMemoryError 。
4.9.2 DeleteWeakGlobalRef
/*** @brief JNIEnv 接口函数表中的索引 227** @param env JNI 接口指针,不得为 NULL* @param obj 要删除的全局弱引用。如果通过 NULL ,则此函数不执行任何操作* @since JDK/JRE 1.2*/
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);
删除给定弱全局引用所需的虚拟机资源。
4.10 对象操作
4.10.1 AllocObject
/*** @brief JNIEnv 接口函数表中的索引 27** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象的引用,不得为 NULL* @return jobject 返回一个 Java 对象,如果无法构建对象,则返回 NULL* @throw InstantiationException 如果类是接口或抽象类* @throw OutOfMemoryError 如果系统内存不足*/
jobject AllocObject(JNIEnv *env, jclass clazz);
分配一个新 Java 对象,但不调用该对象的任何构造函数。返回对象的引用。
注:Java 语言规范 "Implementing Finalization"(JLS §12.6.1)规定"在对象Obj调用了对象的构造函数且调用成功之前,对象Obj不可最终确定"。由于 AllocObject() 没有调用构造函数,因此使用该函数创建的对象不符合最终化的条件。
clazz 参数不得引用数组类。
4.10.2 NewObject, NewObjectA, NewObjectV
/*** @brief JNIEnv 接口函数表中的索引 28* 程序员会将所有要传递给构造函数的参数紧跟在 methodID 参数之后。* NewObject() 会接受这些参数,并将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象的引用,不得为 NULL* @param methodID 构造函数的方法 ID* @param ... 构造函数的参数* @return jobject 返回一个 Java 对象,如果无法构建对象,则返回 NULL* @throw InstantiationException 如果类是接口或抽象类* @throw OutOfMemoryError 如果系统内存不足* @throw 构造函数抛出的任何异常*/
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);/*** @brief JNIEnv 接口函数表中的索引 30* 程序员将所有要传递给构造函数的参数放入一个 args 数组 jvalues 中,该数组紧跟在参数 methodID 之后。* NewObjectA() 会接受这个数组中的参数,并反过来将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象的引用,不得为 NULL* @param methodID 构造函数的方法 ID* @param args 构造函数的参数* @return jobject 返回一个 Java 对象,如果无法构建对象,则返回 NULL* @throw InstantiationException 如果类是接口或抽象类* @throw OutOfMemoryError 如果系统内存不足* @throw 构造函数抛出的任何异常**/
jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);/*** @brief JNIEnv 接口函数表中的索引 29* 程序员会将所有要传递给构造函数的参数放入紧跟 methodID 参数之后的 va_list 类型 args 参数中。* NewObjectV() 会接受这些参数,并将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象的引用,不得为 NULL* @param methodID 构造函数的方法 ID* @param args 构造函数的参数* @return jobject 返回一个 Java 对象,如果无法构建对象,则返回 NULL* @throw InstantiationException 如果类是接口或抽象类* @throw OutOfMemoryError 如果系统内存不足* @throw 构造函数抛出的任何异常*/
jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
构造一个新的 Java 对象。方法 ID 表示要调用的构造函数方法。该 ID 必须通过调用 GetMethodID() 获得,方法名称为 <init> ,返回类型为 void ( V )。
4.10.3 GetObjectClass
/*** @brief JNIEnv 接口函数表中的索引 31** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @return jclass 返回一个 Java 类对象*/
jclass GetObjectClass(JNIEnv *env, jobject obj);
返回对象的类别。
4.10.4 GetObjectRefType
/*** @brief JNIEnv 接口函数表中的索引 232** @param env JNI 接口指针,不得为 NULL* @param obj 本地、全局或弱全局引用* @return jobjectRefType 见下文描述* @since JDK/JRE 1.6*/
jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);
返回参数 obj 所引用对象的类型。参数 obj 可以是本地引用、全局引用或弱全局引用,也可以是 NULL .
返回值:函数 GetObjectRefType 返回以下定义为 jobjectRefType 的枚举值之一:
JNIInvalidRefType = 0
JNILocalRefType = 1
JNIGlobalRefType = 2
JNIWeakGlobalRefType = 3
如果参数 obj 是弱全局引用类型,则返回值为 JNIWeakGlobalRefType。如果参数 obj 是全局引用类型,返回值将是 JNIGlobalRefType。如果参数 obj 是本地引用类型,则返回值为 JNILocalRefType。如果 obj 参数不是有效引用,则此函数的返回值为 JNIInvalidRefType。
无效引用是指不是有效句柄的引用。也就是说, obj 指针地址指向的内存位置不是由 Ref 创建函数分配的,也不是由 JNI 函数返回的。
因此, NULL将是无效引用, GetObjectRefType(env,NULL) 将返回 JNIInvalidRefType
另一方面,空引用(即指向空值的引用)将返回空引用最初创建时的引用类型。
GetObjectRefType不能用于已删除的引用;由于引用通常是作为指向内存数据结构的指针来实现的,而内存数据结构有可能被虚拟机中的任何引用分配服务重复使用,因此一旦被删除, GetObjectRefType 将返回什么值并不明确。
4.10.5 IsInstanceOf
/*** @brief JNIEnv 接口函数表中的索引 32** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,可能是 NULL 值* @param clazz Java 类对象,不得为 NULL* @return jboolean 如果 obj 可以转换为 clazz ,则返回 JNI_TRUE ;否则返回 JNI_FALSE 。 NULL 对象可以转换为任何类*/
jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
测试对象是否是类的实例。
4.10.6 IsSameObject
/*** @brief JNIEnv 接口函数表中的索引 24** @param env JNI 接口指针,不得为 NULL* @param ref1 Java 对象,可以是 NULL* @param ref2 Java 对象,可以是 NULL* @return jboolean 如果 ref1 和 ref2 指向同一个 Java 对象或都是 NULL ,则返回 JNI_TRUE ;否则返回 JNI_FALSE*/
jboolean IsSameObject(JNIEnv *env, jobject ref1, jobject ref2);
测试两个引用是否指向同一个 Java 对象。
4.11 访问对象的字段
4.11.1 GetFieldID
/*** @brief JNIEnv 接口函数表中的索引 94** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param name 以 0 结尾的修改后 UTF-8 字符串表示的字段名称,不得为 NULL* @param sig 字段签名以 0 结尾的修改后 UTF-8 字符串表示,不得为 NULL* @return jfieldID 返回字段 ID,如果操作失败则返回 NULL* @throw NoSuchFieldError 如果找不到指定字段* @throw ExceptionInInitializerError 如果类初始化器因异常而失败* @throw OutOfMemoryError 如果系统内存不足*/
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
返回类的实例(非静态)字段的字段 ID。字段由其名称和签名指定。访问函数 Get<type>Field 和 Set<type>Field 系列使用字段 ID 来检索对象字段。
GetFieldID() 会导致未初始化的类被初始化;
GetFieldID() 不能用于获取数组的长度字段,请使用 GetArrayLength() ;
4.11.2 Get<type>Field Routines
/*** @brief 该访问例程系列返回对象的实例(非静态)字段值。* 要访问的字段由调用 GetFieldID() 得到的字段 ID 指定** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param fieldID 有效的字段标识* @return NativeType 返回字段的内容*/
NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
下表描述了获取<type>字段例程名称和结果类型。应使用字段的 Java 类型替换 Get<type>Field 中的 type,或使用表中的某个实际例程名称,并使用该例程对应的本地类型替换 NativeType。
JNIEnv 接口函数表中的索引:
4.11.3 Set<type>Field Routines
/*** @brief 该系列访问例程设置对象的实例(非静态)字段值。* 要访问的字段由调用 GetFieldID() 得到的字段 ID 指定。** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param fieldID 有效的字段标识* @param value 字段的新值*/
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value);
下表描述了 Set<type>Field 例程名称和值类型。应使用字段的 Java 类型替换 Set<type>Field 中的 type,或使用表中的某个实际例程名称,并使用该例程的相应本地类型替换 NativeType。
JNIEnv 接口函数表中的索引:
4.12 调用实例方法
从本地代码中调用方法时,应注意这些方法是否对调用者敏感。
4.12.1 GetMethodID
/*** @brief 返回类或接口的实例(非静态)方法的方法 ID。* 该方法可能在 clazz 的一个超类中定义,并被 clazz 继承。方法由其名称和签名决定。* JNIEnv 接口函数表中的索引 33** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param name 以 0 结尾的修改后 UTF-8 字符串表示的方法名称,不得为 NULL* @param sig 以 0 结尾的修改 UTF-8 字符串表示的方法签名,不得为 NULL* @return jmethodID 返回方法 ID,如果找不到指定方法,则返回 NULL* @throw NoSuchMethodError NoSuchMethodError* @throw ExceptionInInitializerError 如果类初始化器因异常而失败* @throw OutOfMemoryError 如果系统内存不足*/
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
GetMethodID() 会导致未初始化的类被初始化。
要获取构造函数的方法 ID,请提供 <init> 作为方法名称, void ( V ) 作为返回类型。
4.12.2 Call<type>Method Routines, Call<type>MethodA Routines, Call<type>MethodV Routines
/*** @brief 程序员将所有要传递给方法的参数都紧跟在 methodID 参数之后。* 调用方法例程接受这些参数,并将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param methodID 有效的方法 ID* @param ...* @return NativeType 返回调用 Java 方法的结果*/
NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...);/*** @brief 程序员将方法的所有参数放入一个 args 数组 jvalues 中,该数组紧随参数 methodID 之后。* 调用MethodA 例程接受该数组中的参数,然后将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param methodID 有效的方法 ID* @param args 参数数组* @return NativeType 返回调用 Java 方法的结果*/
NativeType Call<type>MethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);/*** @brief 程序员将方法的所有参数放在紧跟 methodID 参数的 va_list 类型 args 参数中。* 调用MethodV 例程接受参数,然后将参数传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param methodID 有效的方法 ID* @param args 参数数组* @return NativeType NativeType 返回调用 Java 方法的结果*/
NativeType Call<type>MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
这三个操作系列的方法都用于从本地方法中调用 Java 实例方法,它们的区别仅在于向所调用的方法传递参数的机制不同。
这些操作系列根据指定的方法 ID 在 Java 对象上调用实例(非静态)方法。 methodID 参数必须通过调用 GetMethodID() 得到。
当这些函数用于调用私有方法和构造函数时,方法 ID 必须来自 obj 的实际类,而不是它的某个超类。
JNIEnv 接口函数表中的索引:
4.12.3 CallNonvirtual<type>Method Routines, CallNonvirtual<type>MethodA Routines, CallNonvirtual<type>MethodV Routines
/*** @brief 序员将所有要传递给方法的参数放在紧接着 methodID 参数的位置。* CallNonvirtual方法例程接受这些参数,并将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param clazz Java 类,不得为 NULL* @param methodID 方法 ID* @param ...* @return NativeType 返回调用 Java 方法的结果*/
NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);/*** @brief 程序员会将方法的所有参数放入一个 args 数组 jvalues 中,该数组紧随参数 methodID 之后。* CallNonvirtualMethodA 例程接受该数组中的参数,然后将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param clazz Java 类,不得为 NULL* @param methodID 方法 ID* @param args 参数数组* @return NativeType 返回调用 Java 方法的结果*/
NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args);/*** @brief 程序员将方法的所有参数放在紧跟 methodID 参数的 va_list 类型 args 参数中。* CallNonvirtualMethodV 例程接受参数,然后将参数传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param obj Java 对象,不得为 NULL* @param clazz Java 类,不得为 NULL* @param methodID 方法 ID* @param args 1个va_list参数* @return NativeType 返回调用 Java 方法的结果*/
NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args);
这些操作系列根据指定的类和方法 ID 在 Java 对象上调用实例(非静态)方法。 methodID 参数必须通过在类 clazz 上调用 GetMethodID() 来获得。
CallNonvirtual方法系列例程和 Call方法系列例程是不同的。Call方法例程根据对象的类或接口调用方法,而 CallNonvirtual方法例程则根据类(由 clazz 参数指定)调用方法,而方法 ID 就是从类中获取的。方法 ID 必须从对象的实际类或其超类中获取。
下表根据结果类型描述了每个方法调用例程。在调用<type>方法时,应将 type 替换为所调用方法的 Java 类型(或使用表中的一个实际方法调用例程名称),并将 NativeType 替换为该例程的相应本地类型。
JNIEnv 接口函数表中的索引:
4.13 访问静态字段
4.13.1 GetStaticFieldID
/*** @brief 返回类中静态字段的字段 ID。字段由其名称和签名指定。* GetStatic<type>Field 和 SetStatic<type>Field 系列访问函数使用字段 ID 来检索静态字段。* JNIEnv 接口函数表中的索引 144** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param name 以 0 结尾的修改后 UTF-8 字符串表示的静态字段名称,不得为 NULL* @param sig 字段签名以 0 结尾的修改后 UTF-8 字符串表示,不得为 NULL* @return jfieldID 返回字段 ID,如果找不到指定的静态字段,则返回 NULL* @throw NoSuchFieldError 如果找不到指定的静态字段* @throw ExceptionInInitializerError 如果类初始化器因异常而失败* @throw OutOfMemoryError 如果系统内存不足*/
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
GetStaticFieldID() 会导致未初始化的类被初始化。
4.13.2 GetStatic<type>Field Routines
/*** @brief 该访问例程系列返回对象静态字段的值。* 要访问的字段由字段 ID 指定,字段 ID 可通过调用 GetStaticFieldID() 得到* * @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param fieldID 有效的静态字段 ID* @return NativeType 返回静态字段的内容*/
NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID);
该访问例程系列返回对象静态字段的值。要访问的字段由字段 ID 指定,字段 ID 可通过调用 GetStaticFieldID() 得到。
下表描述了获取例程名称和结果类型系列。应将 GetStatic<type>Field 中的 type 替换为字段的 Java 类型或表中的实际静态字段访问例程名称之一,并将 NativeType 替换为该例程的相应本地类型。
JNIEnv 接口函数表中的索引:
4.13.2 SetStatic<type>Field Routines
/*** @brief 该访问例程系列用于设置对象静态字段的值。* 要访问的字段由字段 ID 指定,字段 ID 可通过调用 GetStaticFieldID() 得到。** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param fieldID 有效的静态字段 ID* @param value 字段的新值*/
void SetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value);
下表描述了设置例程名称和值类型。应将 SetStatic<type>Field 中的 type 替换为字段的 Java 类型或表中的实际设置静态字段例程名称之一,并将 NativeType 替换为该例程的相应本地类型。
JNIEnv 接口函数表中的索引:
4.14 调用静态方法
从本地代码中调用方法时,应注意这些方法是否对调用者敏感。
4.14.1 GetStaticMethodID
/*** @brief 返回类中静态方法的方法 ID。方法由其名称和签名指定。* JNIEnv 接口函数表中的索引 113** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param name 以 0 结尾的修改后 UTF-8 字符串表示的静态方法名称,不得为 NULL* @param sig 方法签名以 0 结尾的修改 UTF-8 字符串表示,不得为 NULL* @return jmethodID 返回方法 ID,如果操作失败则返回 NULL* @throw NoSuchMethodError 如果找不到指定的静态方法* @throw ExceptionInInitializerError 如果类初始化器因异常而失败* @throw OutOfMemoryError 如果系统内存不足*/
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
GetStaticMethodID() 会导致未初始化的类被初始化。
4.14.2 CallStatic<type>Method Routines, CallStatic<type>MethodA Routines, CallStatic<type>MethodV Routines
/*** @brief 程序员应将所有要传递给方法的参数紧跟在 methodID 参数之后。* CallStatic方法例程接受这些参数,并将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param methodID 有效的静态方法 ID* @param ... 参数数组* @return NativeType 返回调用静态 Java 方法的结果*/
NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz, jmethodID methodID, ...);/*** @brief 程序员应将方法的所有参数放入一个 args 数组 jvalues 中,该数组紧随参数 methodID 之后。* CallStaticMethodA 例程接受该数组中的参数,然后将它们传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param methodID 有效的静态方法 ID* @param args 参数数组* @return NativeType 返回调用静态 Java 方法的结果*/
NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);/*** @brief 程序员应将方法的所有参数放在紧随 methodID 参数之后的 va_list 类型 args 参数中。* CallStaticMethodV 例程接受参数,然后将参数传递给程序员希望调用的 Java 方法。** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param methodID 有效的静态方法 ID* @param args 1个va_list参数* @return NativeType 返回调用静态 Java 方法的结果*/
NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
JNIEnv 接口函数表中的索引:
4.15 字符串操作
本规范不假定 JVM 内部如何表示 Java 字符串。这些操作返回的字符串:
GetStringChars()
GetStringUTFChars()
GetStringRegion()
GetStringUTFRegion()
GetStringCritical()
因不需要以 NULL 结尾。程序员应通过 GetStringLength() 或 GetStringUTFLength() 来确定缓冲区容量要求。
4.15.1 NewString
/*** @brief 用一个 Unicode 字符数组构造一个新的 java.lang.String 对象。* JNIEnv 接口函数表中的索引 163** @param env JNI 接口指针,不得为 NULL* @param unicodeChars Unicode 字符串指针。可以是 NULL 值,在这种情况下, len 必须是 0* @param len Unicode 字符串的长度。可为 0* @return jstring 返回 Java 字符串对象,如果无法构造字符串,则返回 NULL* @throw OutOfMemoryError 如果系统内存不足*/
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
4.15.2 GetStringLength
/*** @brief 返回 Java 字符串的长度(Unicode 字符数* JNIEnv 接口函数表中的索引 164** @param env JNI 接口指针,不得为 NULL* @param string Java 字符串对象,不得为 NULL* @return jsize 返回 Java 字符串的长度*/
jsize GetStringLength(JNIEnv *env, jstring string);
4.15.3 GetStringChars
/*** @brief 返回指向字符串 Unicode 字符数组的指针。该指针在调用 ReleaseStringChars() 之前一直有效* 如果 isCopy 不是 NULL ,则在复制时将 *isCopy 设置为 JNI_TRUE ;如果不复制,则将 *isCopy 设置为 JNI_FALSE* JNIEnv 接口函数表中的索引 165** @param env JNI 接口指针,不得为 NULL* @param string Java 字符串对象,不得为 NULL* @param isCopy 布尔值指针,可以是 NULL 值* @return const jchar* 返回指向 Unicode 字符串的指针,如果操作失败则返回 NULL*/
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
该指针在调用 ReleaseStringChars() 之前一直有效;
4.15.4 ReleaseStringChars
/*** @brief 通知虚拟机本地代码不再需要访问 chars 。* 参数 chars 是使用 GetStringChars() 从 string 获取的指针* JNIEnv 接口函数表中的索引 166** @param env JNI 接口指针,不得为 NULL* @param string Java 字符串对象,不得为 NULL* @param chars 指向 Unicode 字符串的指针,如之前由 GetStringChars()*/
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
4.15.5 NewStringUTF
/*** @brief 用修改后的 UTF-8 编码字符数组构造一个新的 java.lang.String 对象* JNIEnv 接口函数表中的索引 167** @param env JNI 接口指针,不得为 NULL* @param bytes 指向修改后的 UTF-8 字符串的指针,不得为 NULL* @return jstring 返回 Java 字符串对象,如果无法构造字符串,则返回 NULL* @throw OutOfMemoryError 如果系统内存不足*/
jstring NewStringUTF(JNIEnv *env, const char *bytes);
4.15.6 GetStringUTFLength
/*** @brief 返回字符串的修改后 UTF-8 表示形式的长度(以字节为单位)。* JNIEnv 接口函数表中的索引 168* * @param env JNI 接口指针,不得为 NULL * @param string Java 字符串对象,不得为 NULL* @return jsize 返回字符串的 UTF-8 长度
*/
jsize GetStringUTFLength(JNIEnv *env, jstring string);
4.15.7 GetStringUTFChars
/*** @brief 返回一个指针,指向以修改后的 UTF-8 编码表示字符串的字节数组。* 该数组在被 ReleaseStringUTFChars() 释放前一直有效。* JNIEnv 接口函数表中的索引 169* * @param env JNI 接口指针,不得为 NULL * @param string Java 字符串对象,不得为 NULL* @param isCopy 布尔值指针,可以是 NULL 值* @return 返回指向修改后 UTF-8 字符串的指针;如果操作失败,则返回 NULL
*/
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
如果 isCopy 不是 NULL ,则在复制时将 *isCopy 设置为 JNI_TRUE ;如果不复制,则将 *isCopy 设置为 JNI_FALSE 。
4.15.8 ReleaseStringUTFChars
/*** @brief 通知虚拟机本地代码不再需要访问 utf 。* 参数 utf 是使用 GetStringUTFChars() 从 string 派生的指针。* JNIEnv 接口函数表中的索引 170* * @param env JNI 接口指针,不得为 NULL* @param string Java 字符串对象,不得为 NULL* @param utf 指向修改后的 UTF-8 字符串的指针,之前由 GetStringUTFChars()
*/
void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
注:在 JDK/JRE 1.1 中,程序员可在用户提供的缓冲区中获取原始数组元素。从 JDK/JRE 1.2 开始,本机代码可使用附加函数集在用户提供的缓冲区中获取 Unicode (UTF-16) 或修改后的 UTF-8 编码字符。请参阅下面的函数。
4.15.9 GetStringRegion
/*** @brief 将 len 个从偏移量 start 开始的 Unicode 字符复制到给定的缓冲区 buf* JNIEnv 接口函数表中的索引 220* * @param env JNI 接口指针,不得为 NULL* @param str Java 字符串对象,不得为 NULL* @param start 要复制的字符串中第一个 unicode 字符的索引。必须大于或等于 0,且小于字符串长度(" GetStringLength() ")* @param len 要复制的 unicode 字符数。必须大于或等于零," start + len "必须小于字符串长度(" GetStringLength() ")* @param buf 复制字符串区域的 unicode 字符缓冲区。如果给定值 len > 0,则不能为 NULL* @throw StringIndexOutOfBoundsException 索引溢出时* @since JDK/JRE 1.2
*/
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
4.15.10 GetStringUTFRegion
/*** @brief 将从偏移量 start 开始的 len 个 Unicode 字符转换为修改后的 UTF-8 编码,并将结果放入给定的缓冲区 buf 中* JNIEnv 接口函数表中的索引 221** @param env JNI 接口指针,不得为 NULL* @param str Java 字符串对象,不得为 NULL* @param start 要复制的字符串中第一个 unicode 字符的索引。必须大于或等于零,且小于字符串长度* @param len 要复制的 unicode 字符数。必须大于 0,且 " start + len " 必须小于字符串长度(" GetStringLength() ")* @param buf 复制字符串区域的 unicode 字符缓冲区。如果给定值 len > 0,则不能为 NULL* @throw StringIndexOutOfBoundsException 索引溢出时* @since JDK/JRE 1.2*/
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
len 参数指定 unicode 字符数。修改后的 UTF-8 编码字符数可能大于给定的 len 参数。 GetStringUTFLength() 可以用来确定所需字符缓冲区的最大大小。
由于本规范不要求生成的字符串副本以 NULL 结尾,因此建议在使用该函数前清除给定的字符缓冲区(例如 " memset() "),以便安全地执行 strlen() 。
4.15.11 GetStringCritical, ReleaseStringCritical
/*** @brief JNIEnv 接口函数表中的索引 224* GetStringChars* ReleaseStringChars* @since JDK/JRE 1.2*/
const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);/*** @brief JNIEnv 接口函数表中的索引 225* GetStringChars* ReleaseStringChars* @since JDK/JRE 1.2*/
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);
这两个函数的语义与现有的 Get/ReleaseStringChars 函数类似。如果可能,虚拟机将返回一个指向字符串元素的指针;否则,将进行复制。不过,这些函数的使用方式有很大的限制。在 Get/ReleaseStringCritical 调用所包围的代码段中,本地代码不得发出任意的 JNI 调用,也不得导致当前线程阻塞。
对 Get/ReleaseStringCritical 的限制与对 Get/ReleasePrimitiveArrayCritical 的限制类似。
4.6 Array操作
4.6.1 GetArrayLength
/*** @brief 返回数组中元素的个数* JNIEnv 接口函数表中的索引 171** @param env JNI 接口指针,不得为 NULL* @param array Java 数组对象,不得为 NULL* @return jsize 返回数组的长度*/
jsize GetArrayLength(JNIEnv *env, jarray array);
4.6.2 NewObjectArray
/*** @brief 构造一个新数组,其中包含 elementClass 类对象。所有元素的初始值都设置为 initialElement* JNIEnv 接口函数表中的索引 172** @param env JNI 接口指针,不得为 NULL* @param length 数组大小,必须 >= 0* @param elementClass 数组元素类别,不得为 NULL* @param initialElement 初始化值,可以是 NULL 值* @return jobjectArray 返回一个 Java 数组对象,如果无法构造数组,则返回 NULL* @throw OutOfMemoryError 如果系统内存不足*/
jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
4.6.3 GetObjectArrayElement
/*** @brief 返回 Object 数组的一个元素* JNIEnv 接口函数表中的索引 173** @param env JNI 接口指针,不得为 NULL* @param array Java 数组,不得为 NULL* @param index 数组索引,必须大于等于 0 且小于数组长度(" GetArrayLength() ")* @return jobject 返回一个 Java 对象* @throw ArrayIndexOutOfBoundsException 如果 index 没有指定数组中的有效索引*/
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
4.6.4 SetObjectArrayElement
/*** @brief 设置 Object 数组的一个元素* JNIEnv 接口函数表中的索引 174** @param env JNI 接口指针,不得为 NULL* @param array Java 数组,不得为 NULL* @param index 数组索引,必须大于等于 0 且小于数组长度(" GetArrayLength() ")* @param value 新值,可以是 NULL 值* @throw ArrayIndexOutOfBoundsException 如果 index 没有指定数组中的有效索引* @throw ArrayStoreException 如果 value 的类不是数组元素类的子类*/
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
4.6.5 New<PrimitiveType>Array Routines
/*** @brief 用于构造新的基元数组对象的一系列操作。下表描述了具体的基元数组构造函数。* 应将 New<PrimitiveType>Array 替换为该表中的一个实际原始数组构造函数例程名称,* 并将 ArrayType 替换为该例程的相应数组类型。** @param env JNI 接口指针,不得为 NULL* @param length 数组长度,必须大于等于 0* @return ArrayType 返回一个 Java 数组,如果无法构造数组,则返回 NULL* @throw OutOfMemoryError 如果系统内存不足*/
ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);
JNIEnv 接口函数表中的索引:
4.6.6 Get<PrimitiveType>ArrayElements Routines
/*** @brief 返回原始数组主体的函数族。在调用相应的 Release<PrimitiveType>ArrayElements() 函数之前,结果一直有效。* 由于返回的数组可能是 Java 数组的副本,因此在调用<PrimitiveType>ArrayElements() 之前,对返回数组所做的更改不一定会反映在原始数组中。** @param env JNI 接口指针,不得为 NULL* @param array Java 数组对象,不得为 NULL* @param isCopy 布尔值指针,可以是 NULL 值* @return NativeType* 返回指向数组元素的指针,如果操作失败,则返回 NULL* @throw OutOfMemoryError 如果系统内存不足*/
NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, jboolean *isCopy);
JNIEnv 接口函数表中的索引:
4.6.7 Release<PrimitiveType>ArrayElements Routines
/*** @brief 一系列函数,用于通知虚拟机本地代码不再需要访问 elems 。* elems 参数是使用相应的 GetArrayElements() 函数从 array 派生的指针。如有必要,该函数会将对 elems 所做的所有更改复制回原始数组。** @param env JNI 接口指针,不得为 NULL* @param array Java 数组对象,不得为 NULL* @param elems 数组元素指针,由之前的 GetArrayElements 调用返回* @param mode 释放模式: 0 、 JNI_COMMIT 或 JNI_ABORT*/
void Release<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode);
参数 mode 提供了如何释放数组缓冲区的信息。如果 elems 不是 array 中元素的副本,则 mode 没有影响。否则, mode 会产生如下影响,如下表所示:
在大多数情况下,程序员会将 "0 "作为 mode 参数,以确保“锁定”数组和复制数组的行为一致。其他选项为程序员提供了更多内存管理控制,使用时应格外小心。如果将 JNI_COMMIT 作为 mode 参数传递给 elems ,而 elems 是 array 中元素的副本,则应最后调用 ReleaseArrayElements,将 mode 参数传递为 "0 "或 JNI_ABORT ,以释放 elems 缓冲区。
①. 将 Release<PrimitiveType>ArrayElements 替换为下表中的一个实际原始数组处理程序名称。
②. 将 ArrayType 替换为相应的数组类型。
③. 将 NativeType 替换为该例程的相应本地类型
JNIEnv 接口函数表中的索引:
4.6.8 Get<PrimitiveType>ArrayRegion Routines
/*** @brief 将原始数组的一个区域复制到缓冲区的函数族。** @param env JNI 接口指针,不得为 NULL* @param array Java 数组,不得为 NULL* @param start 起始索引,必须大于或等于零,且小于数组长度( GetArrayLength() )* @param len 要复制的元素个数,必须大于或等于 0," start + len "必须小于数组长度(" GetArrayLength() ")* @param buf 目标缓冲区,不得为 NULL* @throw ArrayIndexOutOfBoundsException 如果区域中的一个索引无效*/
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);
下表描述了特定的基元数组元素访问器。应进行以下替换:
①. 将 Get<PrimitiveType>ArrayRegion 替换为下表中的一个实际原始元素访问例程名称
②. 将 ArrayType 替换为相应的数组类型
③. 将 NativeType 替换为该例程的相应本地类型
JNIEnv 接口函数表中的索引:
4.6.9 Set<PrimitiveType>ArrayRegion Routines
/*** @brief 从缓冲区拷贝回原始数组区域的函数族。** @param env JNI 接口指针,不得为 NULL* @param array Java 数组,不得为 NULL* @param start 起始索引,必须大于或等于零,且小于数组长度( GetArrayLength() )* @param len 要复制的元素个数,必须大于或等于 0," start + len "必须小于数组长度(" GetArrayLength() ")* @param buf 源缓冲区,不得为 NULL* @throw ArrayIndexOutOfBoundsException 如果区域中的一个索引无效*/
void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, const NativeType *buf);
下表描述了特定的基元数组元素访问器。您应进行以下替换:
①. 将 Set<PrimitiveType>ArrayRegion 替换为下表中的一个实际原始元素访问例程名称
②. 将 ArrayType 替换为相应的数组类型
③. 将 NativeType 替换为该例程的相应本地类型
JNIEnv 接口函数表中的索引:
注:程序员可使用 Get/Release<primitivetype>ArrayElements 函数获取指向原始数组元素的指针。如果虚拟机支持“锁定”功能,则会返回原始数据的指针;否则,就会创建一个副本。Get/Release<primitivetype>ArrayCritical 函数允许本地代码直接获取指向数组元素的指针,即使虚拟机不支持“锁定”。
4.6.10 GetPrimitiveArrayCritical, ReleasePrimitiveArrayCritical
/*** @brief 获取原始数组临界值* JNIEnv 接口功能表中的链接索引 222** @param env JNI 接口指针,不得为 NULL* @param array JNI 接口指针,不得为 NULL* @param isCopy 布尔值指针,可以是 NULL 值* @return void* 返回指向数组元素的指针,如果操作失败则返回 NULL* @throw OutOfMemoryError 如果系统内存不足* @since JDK/JRE 1.2*/
void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);/*** @brief 释放原始数组临界值* JNIEnv 接口功能表中的链接索引 223** @param env JNI 接口指针,不得为 NULL* @param array Java 数组,不得为 NULL* @param carray 由 GetPrimitiveArrayCritical 返回的临界数组指针* @param mode 释放模式(参见原始码阵释放模式): 0 , JNI_COMMIT 或 JNI_ABORT 。如果 carray 不是复制,则忽略* @since JDK/JRE 1.2*/
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);
这两个函数的语义与现有的 Get/Release<primitivetype>ArrayElements 函数非常相似。如果可能,虚拟机将返回指向原始数组的指针;否则,将进行复制。不过,这些函数的使用方式有很大的限制。
在调用 GetPrimitiveArrayCritical 之后,本地代码不应在调用 ReleasePrimitiveArrayCritical 之前长时间运行。我们必须将这对函数中的代码视为运行在 "临界区 "中。在临界区内,本地代码不得调用其他 JNI 函数或任何可能导致当前线程阻塞并等待其他 Java 线程的系统调用。(例如,当前线程不得调用另一个 Java 线程正在写入的流上的 read )。
这些限制使得本地代码更有可能获得数组的未拷贝版本,即使虚拟机不支持“锁定”。例如,当本地代码持有通过 GetPrimitiveArrayCritical 获取的数组指针时,虚拟机可能会暂时禁用垃圾回收。
可以嵌套多个 GetPrimtiveArrayCritical 和 ReleasePrimitiveArrayCritical 对。例如:
jint len = (*env)->GetArrayLength(env, arr1);
jbyte *a1 = (*env)->GetPrimitiveArrayCritical(env, arr1, 0);
jbyte *a2 = (*env)->GetPrimitiveArrayCritical(env, arr2, 0);
/* We need to check in case the VM tried to make a copy. */
if (a1 == NULL || a2 == NULL) {... /* out of memory exception thrown */
}
memcpy(a1, a2, len);
(*env)->ReleasePrimitiveArrayCritical(env, arr2, a2, 0);
(*env)->ReleasePrimitiveArrayCritical(env, arr1, a1, 0);
需要注意的是,如果虚拟机内部以不同的格式表示数组, GetPrimitiveArrayCritical 仍有可能复制数组。因此,我们需要对照 NULL 检查其返回值,以防可能出现的内存不足情况。
4.7 注册本地方法
4.7.1 RegisterNatives
/*** @brief JNIEnv 接口函数表中的索引 215** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @param methods 类中的本地方法,不得为 NULL* @param nMethods 类中本地方法的数量,必须大于零* @return jint 成功时返回 "0";失败时返回负值* @throw NoSuchMethodError 如果找不到指定方法或该方法不是本地方法*/
jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
用 clazz 参数指定的类注册本地方法。参数 methods 指定了一个由 JNINativeMethod 个结构组成的数组,其中包含本地方法的名称、签名和函数指针。JNINativeMethod 结构的 name 和 signature 字段是指向修改后的 UTF-8 字符串的指针。 nMethods 参数指定数组中本地方法的数量。 JNINativeMethod 结构定义如下:
typedef struct {char *name;char *signature;void *fnPtr;
} JNINativeMethod;
函数指针名义上必须具有以下签名:
ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...);
请注意,"0"可以通过更改特定本地 Java 方法要执行的本地代码来改变 JVM 的文档行为(包括加密算法、正确性、安全性和类型安全性)。因此,请谨慎使用使用 RegisterNatives 功能的本地库应用程序。
4.7.2 UnregisterNatives
/*** @brief 取消注册类的本地方法。类将回到其本地方法函数被链接或注册前的状态* JNIEnv 接口函数表中的索引 216** @param env JNI 接口指针,不得为 NULL* @param clazz Java 类对象,不得为 NULL* @return jint 成功时返回 "0";失败时返回负值*/
jint UnregisterNatives(JNIEnv *env, jclass clazz);
该函数不应在普通本地代码中使用。相反,它为特殊程序提供了一种重新加载和重新链接本地程序库的方法。
4.8 监控操作
4.8.1 MonitorEnter
/*** @brief 进入与 obj 所引用的底层 Java 对象相关联的监视器* JNIEnv 接口函数表中的索引 217** @param env JNI 接口指针,不得为 NULL* @param obj 普通 Java 对象或类对象,不得为 NULL* @return jint 成功时返回 "0";失败时返回负值*/
jint MonitorEnter(JNIEnv *env, jobject obj);
输入与 obj 所指对象相关的监视器。 obj 引用必须不是 NULL 。
每个 Java 对象都有一个与之关联的监视器。如果当前线程已拥有与 obj 关联的监视器,则会递增监视器中的计数器,以显示该线程进入监视器的次数。如果与 obj 关联的监视器未被任何线程拥有,则当前线程将成为该监视器的所有者,并将该监视器的进入计数设为 1。 如果与 obj 关联的监视器已被其他线程拥有,则当前线程将等待监视器被释放,然后再次尝试获得所有权。
通过 MonitorEnter JNI 函数调用进入的监控器不能使用 monitorexit Java 虚拟机指令或同步方法返回退出。 MonitorEnter JNI 函数调用和 monitorenter Java 虚拟机指令可能会竞相进入与同一对象相关的监控器。
为避免死锁,通过 MonitorEnter JNI 函数调用进入的监视器必须使用 MonitorExit JNI 调用退出,除非 DetachCurrentThread 调用用于隐式释放 JNI 监视器。
4.8.2 MonitorExit
/*** @brief 当前线程必须是与 obj 所引用的底层 Java 对象相关联的监视器的所有者。* 线程会递减计数器,以显示进入该监视器的次数。如果计数器的值为零,则当前线程释放监视器。* JNIEnv 接口函数表中的索引 218** @param env JNI 接口指针,不得为 NULL* @param obj 普通 Java 对象或类对象,不得为 NULL* @return jint 成功时返回 "0";失败时返回负值* @throw IllegalMonitorStateException 如果当前线程不拥有监视器*/
jint MonitorExit(JNIEnv *env, jobject obj);
本地代码不得使用 MonitorExit 退出通过同步方法或 monitorenter Java 虚拟机指令进入的监控器。
4.9 支持NIO
与 NIO 相关的入口点允许本地代码访问java.nio
直接缓冲区。直接缓冲区的内容有可能位于普通垃圾堆之外的本地内存中。有关直接缓冲区的信息,请参阅 NIO 包中的缓冲区和 java.nio.ByteBuffer 类的规范。
有三个函数允许 JNI 代码创建、检查和操作直接缓冲区:
NewDirectByteBuffer
GetDirectBufferAddress
GetDirectBufferCapacity
Java 虚拟机的每个实现都必须支持这些函数,但并非每个实现都必须支持 JNI 对直接缓冲区的访问。如果 JVM 不支持此类访问,那么 NewDirectByteBuffer 和 GetDirectBufferAddress 函数必须始终返回 NULL , GetDirectBufferCapacity 函数必须始终返回 -1 。如果 JVM 支持这种访问,则必须实现这三个函数以返回相应的值。
4.9.1 NewDirectByteBuffer
/*** @brief 分配并返回一个直接指向内存块的 java.nio.ByteBuffer ,该内存块从内存地址 address 开始,扩展 capacity 个字节。* 返回缓冲区的字节顺序始终是大三位(高字节在前; java.nio.ByteOrder.BIG_ENDIAN )。* JNIEnv 接口函数表中的索引 229** @param env JNI 接口指针,不得为 NULL* @param address 内存区域的起始地址,不得为 NULL* @param capacity 内存区域的大小(以字节为单位),必须为非负数且小于或等于 Integer.MAX_VALUE* @return jobject 返回新创建的 java.nio.ByteBuffer 对象的本地引用。如果出现异常,或该虚拟机不支持 JNI 访问直接缓冲区,则返回 NULL* @throw IllegalArgumentException 如果 capacity 为负数或大于 Integer.MAX_VALUE* @throw OutOfMemoryError 如果 ByteBuffer个对象的分配失败* @since JDK/JRE 1.4*/
jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);
4.9.2 GetDirectBufferAddress
/*** @brief 获取并返回给定指令 java.nio.Buffer 引用的内存区域的起始地址* JNIEnv 接口函数表中的索引 230** @param env JNI 接口指针,不得为 NULL* @param buf 直接 java.nio.Buffer 对象,不得 NULL* @return void* 返回缓冲区引用的内存区域的起始地址。如果内存区域未定义,或者给定对象不是直接 java.nio.Buffer ,或者该虚拟机不支持 JNI 访问直接缓冲区,则返回 NULL* @since JDK/JRE 1.4*/
void* GetDirectBufferAddress(JNIEnv* env, jobject buf);
该函数允许本地代码访问 Java 代码通过缓冲区对象访问的同一内存区域。
4.9.3 GetDirectBufferCapacity
/*** @brief 获取并返回给定指令 java.nio.Buffer 所引用的内存区域的容量。容量是内存区域包含的元素数量* JNIEnv 接口函数表中的索引 231** @param env JNI 接口指针,不得为 NULL* @param buf 直接 java.nio.Buffer 对象,不得 NULL* @return jlong 返回与缓冲区相关联的内存区域的容量。如果给定对象不是直接 java.nio.Buffer ,* 如果对象是未对齐视图缓冲区且处理器架构不支持未对齐访问,或者该虚拟机不支持 JNI 访问直接缓冲区,则返回 -1* @since JDK/JRE 1.4*/
jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf);
4.10 支持反射
如果程序员知道 Java 方法或字段的名称和类型,就可以使用 JNI 调用 Java 方法或访问 Java 字段。Java Core Reflection API 允许程序员在运行时反射 Java 类。JNI 提供了一组转换函数,将 JNI 中使用的字段和方法 ID 转换为 Java Core Reflection API 中使用的字段和方法对象。
4.10.1 FromReflectedMethod
/*** @brief 将 java.lang.reflect.Method 或 java.lang.reflect.Constructor 对象转换为方法 ID* JNIEnv 接口函数表中的索引 7** @param env JNI 接口指针,不得为 NULL* @param method java.lang.reflect.Method 或 java.lang.reflect.Constructor 的对象,不得为 NULL* @return jmethodID 与给定 Java 反射方法相对应的 JNI 方法 ID,如果操作失败则为 NULL* @since JDK/JRE 1.2*/
jmethodID FromReflectedMethod(JNIEnv *env, jobject method);
4.10.2 FromReflectedField
/*** @brief 将 java.lang.reflect.Field 转换为字段 ID。* JNIEnv 接口函数表中的索引 8** @param env JNI 接口指针,不得为 NULL* @param field 1 个java.lang.reflect.Field对象,不得为 NULL* @return jfieldID 与给定 Java 反射 field 相对应的 JNI 字段 ID,如果操作失败,则为 NULL 。* @since JDK/JRE 1.2*/
jfieldID FromReflectedField(JNIEnv *env, jobject field);
4.10.3 ToReflectedMethod
/*** @brief 将从 cls 派生的方法 ID 转换为 java.lang.reflect.Method 或 java.lang.reflect.Constructor 对象。* 如果方法 ID 指向静态字段, isStatic 必须设为 JNI_TRUE ,否则设为 JNI_FALSE* JNIEnv 接口函数表中的索引 9** @param env JNI 接口指针,不得为 NULL* @param cls Java 类对象,不得为 NULL* @param methodID 方法 ID,不得为 NULL* @param isStatic 表示给定的 methodID 是否为静态方法* @return jobject 返回与给定的 methodID 相对应的 java.lang.reflect.Method 或 java.lang.reflect.Constructor 的实例,如果操作失败,则返回 NULL* @since JDK/JRE 1.2*/
jobject ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);
如果失败,则抛出 OutOfMemoryError 并返回 0。
4.10.4 ToReflectedField
/*** @brief 将从 cls 派生的字段 ID 转换为 java.lang.reflect.Field 对象。* 如果 fieldID 指向静态字段,则 isStatic 必须设置为 JNI_TRUE ,否则为 JNI_FALSE 。* JNIEnv 接口函数表中的索引 12** @param env JNI 接口指针,不得为 NULL* @param cls Java 类对象,不得为 NULL* @param fieldID 字段 ID,不得为 NULL* @param isStatic 表示给定的 fieldID 是否为静态字段* @return jobject 返回与给定的 fieldID 相对应的 java.lang.reflect.Field 的实例,如果操作失败,则返回 NULL* @since JDK/JRE 1.2*/
jobject ToReflectedField(JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);
如果失败,则抛出 OutOfMemoryError 并返回 0。
4.11 虚拟机接口
4.11.1 GetJavaVM
/*** @brief 返回与当前线程相关的 Java VM 接口(用于调用 API)。* 结果将放置在第二个参数 vm 所指向的位置* JNIEnv 接口函数表中的索引 219** @param env JNI 接口指针,不得为 NULL* @param vm 指向结果放置位置的指针,不得为 NULL* @return jint 成功时返回 "0";失败时返回负值。*/
jint GetJavaVM(JNIEnv *env, JavaVM **vm);
下一篇: 05-调用API