上一篇: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 函数指针的结构的指针。其定义如下:
cpp
typedef const struct JNINativeInterface *JNIEnv;
++虚拟机将初始化函数表++,如下代码示例所示。请注意,前三个条目是为将来与 COM 兼容而保留的。此外,我们还在函数表开头附近保留了一些额外的 NULL 条目,以便将来与类相关的 JNI 操作可以添加到 FindClass 之后(前),而不是表的末尾。
cpp
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 布尔值
cpp
#define JNI_FALSE 0
#define JNI_TRUE 1
4.2.2 返回值
JNI 函数的一般返回值常量。
cpp
#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 获取版本
cpp
/**
* @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 版本常量
cpp
#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
cpp
/**
* @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
cpp
/**
* @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 类的全称是:
cpp
"java/lang/String"
数组类 java.lang.Object[] 的数组类型签名是:
cpp
"[Ljava/lang/Object;"
4.4.3 GetSuperclass
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @brief JNIEnv 接口函数表中的索引 15
*
* @param env JNI 接口指针,不得为 NULL
* @return jthrowable 返回当前正在抛出的异常对象,如果当前没有异常抛出,则返回 NULL
*/
jthrowable ExceptionOccurred(JNIEnv *env);
确定是否有异常抛出。++在本地代码调用 ExceptionClear() 或 Java 代码处理异常之前,异常不会被抛出++。
4.7.4 ExceptionDescribe
cpp
/**
* @brief JNIEnv 接口函数表中的索引 16
*
* @param env JNI 接口指针,不得为 NULL
*/
void ExceptionDescribe(JNIEnv *env);
向系统错误报告通道(如 stderr )打印异常和堆栈回溯。++调用该函数的副作用是清除挂起异常++。这是一个用于调试的方便例程。
4.7.5 ExceptionClear
cpp
/**
* @brief JNIEnv 接口函数表中的索引 17
*
* @param env JNI 接口指针,不得为 NULL
*/
void ExceptionClear(JNIEnv *env);
清除当前抛出的任何异常。如果当前没有异常抛出,则此例程没有任何作用。
4.7.6 FatalError
cpp
/**
* @brief JNIEnv 接口函数表中的索引 18
*
* @param env JNI 接口指针,不得为 NULL
* @param msg 错误信息。字符串以修改后的 UTF-8 编码。可能是 NULL 值。
*/
void FatalError(JNIEnv *env, const char *msg);
引发致命错误,虚拟机无法恢复。此函数不会返回。
4.7.7 ExceptionCheck
cpp
/**
* @brief JNIEnv 接口函数表中的索引 228
*
* @param env JNI 接口指针,不得为 NULL
* @return jboolean 当有异常等待处理时,返回 JNI_TRUE ;否则,返回 JNI_FALSE
*/
jboolean ExceptionCheck(JNIEnv *env);
我们引入了一个方便的函数来检查待处理的异常,而无需创建对异常对象的本地引用。
4.8 全局与局部引用
㈠. 全局引用
4.8.1 NewGlobalRef
cpp
/**
* @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
cpp
/**
* @brief JNIEnv 接口函数表中的索引 22
*
* @param env JNI 接口指针,不得为 NULL
* @param globalRef 全局引用。可能是 NULL 值,在这种情况下,该函数不执行任何操作
*/
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
删除 globalRef 指向的全局引用
㈡. 局部引用
本地引用在本地方法调用期间有效。++本地方法返回后,它们会被自动释放++ 。++每个本地引用都会耗费一定的 Java 虚拟机资源。程序员需要确保本地方法不会过度分配本地引用++。虽然本地引用会在本地方法返回 Java 后自动释放,但++过度分配本地引用可能会导致本地方法执行期间虚拟机内存不足++。
4.8.3 DeleteLocalRef
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @brief JNIEnv 接口函数表中的索引 31
*
* @param env JNI 接口指针,不得为 NULL
* @param obj Java 对象,不得为 NULL
* @return jclass 返回一个 Java 类对象
*/
jclass GetObjectClass(JNIEnv *env, jobject obj);
返回对象的类别。
4.10.4 GetObjectRefType
cpp
/**
* @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 的枚举值之一:
cpp
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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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 字符串。这些操作返回的字符串:
cpp
GetStringChars()
GetStringUTFChars()
GetStringRegion()
GetStringUTFRegion()
GetStringCritical()
++因不需要以 NULL 结尾。程序员应通过 GetStringLength() 或 GetStringUTFLength() 来确定缓冲区容量要求++。
4.15.1 NewString
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @brief 返回数组中元素的个数
* JNIEnv 接口函数表中的索引 171
*
* @param env JNI 接口指针,不得为 NULL
* @param array Java 数组对象,不得为 NULL
* @return jsize 返回数组的长度
*/
jsize GetArrayLength(JNIEnv *env, jarray array);
4.6.2 NewObjectArray
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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 对。例如:
cpp
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
cpp
/**
* @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 结构定义如下:
cpp
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
函数指针名义上必须具有以下签名:
cpp
ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...);
请注意,++"0"可以通过更改特定本地 Java 方法要执行的本地代码来改变 JVM 的文档行为(包括加密算法、正确性、安全性和类型安全性)++。因此,请谨慎使用使用 RegisterNatives 功能的本地库应用程序。
4.7.2 UnregisterNatives
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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 代码创建、检查和操作直接缓冲区:
cpp
NewDirectByteBuffer
GetDirectBufferAddress
GetDirectBufferCapacity
Java 虚拟机的每个实现都必须支持这些函数,但并非每个实现都必须支持 JNI 对直接缓冲区的访问。如果 JVM 不支持此类访问,那么 NewDirectByteBuffer 和 GetDirectBufferAddress 函数必须始终返回 NULL , GetDirectBufferCapacity 函数必须始终返回 -1 。如果 JVM 支持这种访问,则必须实现这三个函数以返回相应的值。
4.9.1 NewDirectByteBuffer
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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
cpp
/**
* @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