天天看点

JNI 函数 访问静态字段、调用静态方法、字符串操作、数组操作

作者:睿智的海边风浪

访问静态字段

GetStaticFieldID

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);           

获取类的静态字段ID。该字段由其名称和签名指定。GetStatic<type>Field和SetStatic<type>Field访问器函数系列使用字段ID来检索静态字段。

GetStaticFieldID()会导致未初始化的类被初始化。

链接:

  • JNIEnv接口函数表中的索引为144。

参数:

  • env:JNI接口指针。
  • clazz:Java类对象。
  • name:静态字段名称,以0终止的修改后的UTF-8字符串。
  • sig:字段签名,以0终止的修改后的UTF-8字符串。

返回值:

  • 返回字段ID,如果找不到指定的静态字段,则返回NULL。

抛出:

  • NoSuchFieldError:如果找不到指定的静态字段。
  • ExceptionInInitializerError:如果类初始化程序由于异常而失败。
  • OutOfMemoryError:如果系统内存不足。

GetStatic<type>Field 例程

NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID);           

该访问器例程系列返回对象的静态字段的值。要访问的字段由字段ID指定,该字段ID通过调用 GetStaticFieldID() 获得。

以下表格描述了获取例程名称和结果类型的系列。您应该将GetStatic<type>Field中的“type”替换为字段的Java类型或来自表格的实际静态字段访问器例程名称之一,并将NativeType替换为该例程的相应本机类型。

GetStatic<type>Field访问器例程系列

GetStatic<type>Field Routine Name Native Type
GetStaticObjectField() jobject
GetStaticBooleanField() jboolean
GetStaticByteField() jbyte
GetStaticCharField() jchar
GetStaticShortField() jshort
GetStaticIntField() jint
GetStaticLongField() jlong
GetStaticFloatField() jfloat
GetStaticDoubleField() jdouble

参数:

  • env:JNI接口指针。
  • clazz:Java类对象。
  • fieldID:静态字段ID。

返回值:

  • 返回静态字段的内容。

SetStatic<type>Field例程

void SetStatic<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID, NativeType value);           

该访问器例程系列设置对象的静态字段值。要访问的字段由字段ID指定,该字段ID通过调用GetStaticFieldID()获得。

以下表格描述了设置例程名称和值类型。您应该将SetStatic<type>Field中的“type”替换为字段的Java类型或来自表格的实际设置静态字段例程名称之一,并将NativeType替换为该例程的相应本机类型。

SetStatic<type>Field访问器例程系列

GetStatic<type>Field Routine Name Native Type
SetStaticObjectField() jobject
SetStaticBooleanField() jboolean
SetStaticByteField() jbyte
SetStaticCharField() jchar
SetStaticShortField() jshort
SetStaticIntField() jint
SetStaticLongField() jlong
SetStaticFloatField() jfloat
SetStaticDoubleField() jdouble

参数:

  • env:JNI接口指针。
  • clazz:Java类对象。
  • fieldID:静态字段ID。
  • value:字段的新值。

调用静态方法

GetStaticMethodID

jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);           

获取类的静态方法ID。该方法由其名称和签名指定。

GetStaticMethodID()会导致未初始化的类被初始化。

链接:

  • JNIEnv接口函数表中的索引为113。

参数:

  • env:JNI接口指针。
  • clazz:Java类对象。
  • name:静态方法名称,以0终止的修改后的UTF-8字符串。
  • sig:方法签名,以0终止的修改后的UTF-8字符串。

返回值:

  • 返回方法ID,如果操作失败,则返回NULL。

抛出:

  • NoSuchMethodError:如果找不到指定的静态方法。
  • ExceptionInInitializerError:如果类初始化程序由于异常而失败。
  • OutOfMemoryError:如果系统内存不足。

CallStatic<type>Method例程、 CallStatic<type>MethodA例程、 CallStatic<type>MethodV例程

NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);

NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz,
jmethodID methodID, jvalue *args);

NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);           

该操作系列根据指定的方法ID在Java对象上调用静态方法。methodID参数必须通过调用GetStaticMethodID() 获得。

方法ID必须从clazz派生,而不是从其超类中派生。

CallStatic<type>Method例程

  • 程序员应该将要传递给方法的所有参数立即放在methodID参数后面。CallStatic<type>Method例程接受这些参数并将它们传递给程序员希望调用的Java方法。

CallStatic<type>MethodA例程

  • 程序员应该将要传递给方法的所有参数放在一个jvalues数组中,该数组紧随methodID参数。CallStaticMethodA例程接受该数组中的参数,并将它们传递给程序员希望调用的Java方法。

CallStatic<type>MethodV例程

  • 程序员应该将要传递给方法的所有参数放在一个va_list类型的args参数中,该参数紧随methodID参数。CallStaticMethodV例程接受这些参数,并将它们传递给程序员希望调用的Java方法。

以下表格根据其结果类型描述了每个方法调用例程。您应该将CallStatic<type>Method中的“type”替换为方法的Java类型或来自表格的实际方法调用例程名称之一,并将NativeType替换为该例程的相应本机类型。

CallStatic<type>Method调用例程

CallStatic<type>Method Routine Name Native Type
CallStaticVoidMethod() CallStaticVoidMethodA() CallStaticVoidMethodV() void
CallStaticObjectMethod() CallStaticObjectMethodA() CallStaticObjectMethodV() jobject
CallStaticBooleanMethod() CallStaticBooleanMethodA() CallStaticBooleanMethodV() jboolean
CallStaticByteMethod() CallStaticByteMethodA() CallStaticByteMethodV() jbyte
CallStaticCharMethod() CallStaticCharMethodA() CallStaticCharMethodV() jchar
CallStaticShortMethod() CallStaticShortMethodA() CallStaticShortMethodV() jshort
CallStaticIntMethod() CallStaticIntMethodA() CallStaticIntMethodV() jint
CallStaticLongMethod() CallStaticLongMethodA() CallStaticLongMethodV() jlong
CallStaticFloatMethod() CallStaticFloatMethodA() CallStaticFloatMethodV() jfloat
CallStaticDoubleMethod() CallStaticDoubleMethodA() CallStaticDoubleMethodV() jdouble

参数:

  • env:JNI接口指针。
  • clazz:Java类对象。
  • methodID:静态方法ID。

CallStatic<type>Method例程的额外参数:

  • 静态方法的参数。

CallStatic<type>MethodA例程的额外参数:

  • args:参数数组。

CallStatic<type>MethodV例程的额外参数:

  • args:参数的va_list。

返回值:

  • 返回调用静态Java方法的结果。

抛出:

  • Java方法执行期间引发的异常。

字符串操作

NewString

jstring NewString(JNIEnv *env, const jchar *unicodeChars,
jsize len);           

从Unicode字符数组构造一个新的java.lang.String对象。

链接:

  • JNIEnv接口函数表中的索引为163。

参数:

  • env:JNI接口指针。
  • unicodeChars:指向Unicode字符串的指针。
  • len:Unicode字符串的长度。

返回值:

  • 返回Java字符串对象,如果无法构造字符串,则返回NULL。

抛出:

  • OutOfMemoryError:如果系统内存不足。

GetStringLength

jsize GetStringLength(JNIEnv *env, jstring string);           

返回Java字符串的长度(Unicode字符数)。

链接:

  • JNIEnv接口函数表中的索引为164。

参数:

  • env:JNI接口指针。
  • string:Java字符串对象。

返回值:

  • 返回Java字符串的长度。

GetStringChars

const jchar * GetStringChars(JNIEnv *env, jstring string,
jboolean *isCopy);           

返回指向字符串的Unicode字符数组的指针。此指针在调用ReleaseStringChars()之前有效。

如果isCopy不为NULL,则如果进行了复制,则* isCopy设置为JNI_TRUE;如果没有进行复制,则设置为JNI_FALSE。

链接:

  • JNIEnv接口函数表中的索引为165。

参数:

  • env:JNI接口指针。
  • string:Java字符串对象。
  • isCopy:指向布尔值的指针。

返回值:

  • 返回指向Unicode字符串的指针,如果操作失败,则返回NULL。

ReleaseStringChars

void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);           

该函数通知虚拟机,本地代码不再需要访问chars。chars参数是通过使用GetStringChars()从string获得的指针。

链接: JNIEnv接口函数表中的索引166。

参数:

  • env:JNI接口指针。
  • string:Java字符串对象。
  • chars:指向Unicode字符串的指针。

NewStringUTF

jstring NewStringUTF(JNIEnv *env, const char *bytes);           

NewStringUTF 函数根据 UTF-8 编码的字符数组构造一个新的 java.lang.String 对象。

链接:

  • JNIEnv 接口函数表中的第 167 项。

参数:

  • env:JNI 接口指针。
  • bytes:指向 UTF-8 编码的字符串的指针。

返回值:

  • 返回一个 Java 字符串对象,如果无法构造该字符串,则返回 NULL。

抛出异常:

  • OutOfMemoryError:如果系统内存不足。

GetStringUTFLength

jsize GetStringUTFLength(JNIEnv *env, jstring string);           

获取字符串的修改后的 UTF-8 编码表示的字节数。

连接:

  • JNIEnv 接口函数表中的第 168 个索引。

参数:

  • env:JNI 接口指针。
  • string:Java 字符串对象。

返回值:

  • 返回字符串的 UTF-8 长度。

GetStringUTFChars

const char * GetStringUTFChars(JNIEnv *env, jstring string,
jboolean *isCopy);           

函数功能:

  • 返回一个指向以修改过的UTF-8编码表示的字符串的字节数组的指针。这个数组在通过ReleaseStringUTFChars()释放之前都是有效的。如果isCopy不是NULL,则*isCopy在进行复制时将被设置为JNI_TRUE;如果没有复制,则将其设置为JNI_FALSE。

LINKAGE:

  • JNIEnv接口函数表中的第169个索引。

参数:

  • env:JNI接口指针。
  • string:一个Java字符串对象。
  • isCopy:一个指向布尔值的指针。

返回值:

  • 返回一个指向修改过的UTF-8字符串的指针,如果操作失败,则返回NULL。

ReleaseStringUTFChars

void ReleaseStringUTFChars(JNIEnv *env, jstring string,
const char *utf);           

功能:

  • 通知虚拟机本地代码不再需要访问 utf。utf 参数是从字符串使用 GetStringUTFChars() 获得的指针。

链接:

  • JNIEnv 接口函数表中的索引 170。

参数:

  • env:JNI 接口指针。
  • string:一个 Java 字符串对象。
  • utf:指向一个修改过的 UTF-8 字符串的指针。

注:

  • 在 JDK/JRE 1.1 中,程序员可以在用户提供的缓冲区中获取基本数组元素。自 JDK/JRE 1.2 开始,提供了额外的一组函数,允许本地代码在用户提供的缓冲区中获取 Unicode(UTF-16)或修改过的 UTF-8 编码的字符。请参阅下面的函数。

GetStringRegion:

void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);           

功能:

  • 将从偏移量start开始的len个Unicode字符复制到给定的缓冲区buf中。
  • 如果索引溢出,则抛出StringIndexOutOfBoundsException异常。

链接:

  • JNIEnv接口函数表中的索引为220。 自JDK/JRE 1.2起可用。

GetStringUTFRegion:

void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);           

功能:

  • 将从偏移量start开始的len个Unicode字符转换为修改后的UTF-8编码,并将结果放置在给定的缓冲区buf中。
  • 如果索引溢出,则抛出StringIndexOutOfBoundsException异常。

链接:

  • JNIEnv接口函数表中的索引为221。 自JDK/JRE 1.2起可用。

GetStringCritical和ReleaseStringCritical函数:

const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);           

功能:

  • 这两个函数的语义与现有的Get/ReleaseStringChars函数类似。如果可能,虚拟机将返回一个指向字符串元素的指针;否则,将进行复制。然而,这些函数的使用受到显著限制。在由Get/ReleaseStringCritical调用包围的代码段中,本机代码不能发出任意JNI调用,或导致当前线程阻塞。Get/ReleaseStringCritical的限制与Get/ReleasePrimitiveArrayCritical的限制类似。

链接(GetStringCritical):

  • JNIEnv接口函数表中的索引为224。

链接(ReleaseStingCritical):

  • JNIEnv接口函数表中的索引为225。

自JDK/JRE 1.2起可用。

数组操作

GetArrayLength

jsize GetArrayLength(JNIEnv *env, jarray array);           

功能:

  • 返回数组中的元素个数。

链接:

  • JNIEnv 接口函数表中的索引为 171。

参数:

  • env:JNI 接口指针。
  • array:Java 数组对象。

返回值:

  • 返回数组的长度。

NewObjectArray

jobjectArray NewObjectArray(JNIEnv *env, jsize length,
jclass elementClass, jobject initialElement);           

功能:

  • 构造一个新的对象数组,其元素为类 elementClass。所有元素的初始值都设置为 initialElement。

链接:

  • JNIEnv 接口函数表中的索引为 172。

参数:

  • env:JNI 接口指针。
  • length:数组大小。
  • elementClass:数组元素类。
  • initialElement:初始化值。

返回值:

  • 返回 Java 数组对象,如果无法构造数组,则返回 NULL。

异常:

  • OutOfMemoryError:如果系统内存不足。

GetObjectArrayElement

jobject GetObjectArrayElement(JNIEnv *env,
jobjectArray array, jsize index);           

功能:

  • 获取一个对象数组的元素。

连接(LINKAGE):

  • JNIEnv接口函数表中的索引173。

参数(PARAMETERS):

  • env:JNI接口指针。
  • array:一个Java数组。
  • index:数组下标。

返回值(RETURNS):

  • 返回一个Java对象。

抛出异常(THROWS):

  • 如果index没有在数组中指定一个有效的索引,则会抛出ArrayIndexOutOfBoundsException异常。

SetObjectArrayElement

void SetObjectArrayElement(JNIEnv *env, jobjectArray array,
jsize index, jobject value);           

功能

  • 设置对象数组中的一个元素。

链接:

  • JNIEnv接口函数表中的第174项。

参数:

  • env:JNI接口指针。
  • array:Java数组。
  • index:数组下标。
  • value:新的值。

抛出:

  • ArrayIndexOutOfBoundsException:如果index不是数组中的有效下标。
  • ArrayStoreException:如果value的类不是数组元素类的子类。

New<PrimitiveType>Array 系列函数

ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);           

功能:

  • New<PrimitiveType>Array是一组操作,用于构造新的原始数组对象。以下表格描述了特定原始数组构造函数。您应该将New<PrimitiveType>Array替换为来自此表中实际原始数组构造函数例程之一的名称,并将ArrayType替换为该例程的相应数组类型。

New<PrimitiveType>Array原始数组构造函数组

New<PrimitiveType>Array Routines Array Type
NewBooleanArray() jbooleanArray
NewByteArray() jbyteArray
NewCharArray() jcharArray
NewShortArray() jshortArray
NewIntArray() jintArray
NewLongArray() jlongArray
NewFloatArray() jfloatArray
NewDoubleArray() jdoubleArray

参数:

  • env:JNI接口指针。
  • length:数组长度。

返回值:

  • 返回一个Java数组对象,如果无法构造该数组,则返回NULL。

Get<PrimitiveType>ArrayElements 的函数族

NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array, jboolean *isCopy);           

功能:

  • 这是一个函数族,用于返回原始类型数组的内容。返回结果在调用相应的ReleaseArrayElements()函数之前是有效的。由于返回的数组可能是Java数组的副本,所以对返回的数组所做的更改不一定会反映在原始数组中,直到调用ReleaseArrayElements()函数。
  • 如果isCopy不为NULL,则如果复制了数组,则*isCopy设置为JNI_TRUE;如果没有复制,则设置为JNI_FALSE。

下表描述了具体的原始类型数组元素访问器。您应该进行以下替换:

  • 用以下表中的实际原始元素访问例程名称之一替换GetArrayElements。
  • 用相应的数组类型替换ArrayType。
  • 用相应的本地类型替换NativeType。

无论布尔数组在Java虚拟机中如何表示,GetBooleanArrayElements()始终返回指向jbooleans的指针,其中每个字节表示一个元素(展开表示)。所有其他类型的数组都保证在内存中是连续的。

Get<PrimitiveType>ArrayElements 访问系列函数

Get<PrimitiveType>ArrayElements Routines Array Type Native Type
GetBooleanArrayElements() jbooleanArray jboolean
GetByteArrayElements() jbyteArray jbyte
GetCharArrayElements() jcharArray jchar
GetShortArrayElements() jshortArray jhort
GetIntArrayElements() jintArray jint
GetLongArrayElements() jlongArray jlong
GetFloatArrayElements() jfloatArray jfloat
GetDoubleArrayElements() jdoubleArray jdouble

参数:

  • env:JNI 接口指针。
  • array:Java 字符串对象。
  • isCopy:一个指向布尔值的指针,用于指示是否复制了数组元素。

返回值:

  • 如果操作成功,返回指向数组元素的指针;否则返回 NULL。

Release<PrimitiveType>ArrayElements例程

void Release<PrimitiveType>ArrayElements(JNIEnv *env,
ArrayType array, NativeType *elems, jint mode);           

功能:

  • 这是一组函数,用于通知虚拟机本地代码不再需要访问 elems 数组。elems 参数是一个指针,由使用相应的 Get<PrimitiveType>ArrayElements() 函数从数组派生而来。如果必要,此函数会将所有对 elems 所做的更改复制回原始数组。
  • mode 参数提供有关如何释放数组缓冲区的信息。如果 elems 不是数组中元素的副本,则 mode 不起作用。否则,mode 具有以下影响,如下表所示:

基本数组释放模式

mode 操作
复制回内容并释放 elems 缓冲区
JNI_COMMIT 复制回内容但不释放 elems 缓冲区
JNI_ABORT 释放缓冲区而不复制可能的更改

在大多数情况下,程序员将“0”传递给 mode 参数,以确保固定和复制数组的行为一致。其他选项为程序员提供了更多关于内存管理的控制,应谨慎使用。

下表描述了组成基本数组释放器系列的特定例程。您应进行以下替换:

  • 将 Release<PrimitiveType>ArrayElements 替换为以下表中的实际基本数组释放例程名称之一。
  • 将 ArrayType 替换为相应的数组类型。
  • 将 NativeType 替换为该例程的相应本机类型。

基本数组释放例程系列 Release<PrimitiveType>ArrayElements

Release<PrimitiveType>ArrayElements例程 数组类型 本机类型
ReleaseBooleanArrayElements() jbooleanArray jboolean
ReleaseByteArrayElements() jbyteArray jbyte
ReleaseCharArrayElements() jcharArray jchar
ReleaseShortArrayElements() jshortArray jshort
ReleaseIntArrayElements() jintArray jint
ReleaseLongArrayElements() jlongArray jlong
ReleaseFloatArrayElements() jfloatArray jfloat
ReleaseDoubleArrayElements() jdoubleArray jdouble

参数:

  • env:JNI接口指针。
  • array:Java数组对象。
  • elems:指向数组元素的指针。
  • mode:释放模式。

Get<PrimitiveType>ArrayRegion函数族

void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
jsize start, jsize len, NativeType *buf);           

功能:

  • 是一组用于将原生数组的一部分复制到缓冲区的函数。

以下表格描述了具体的原生数组元素访问器。您应该进行以下替换:

  • 将Get<PrimitiveType>ArrayRegion替换为以下表格中实际的原始元素访问器例程之一。
  • 将ArrayType替换为相应的数组类型。
  • 将NativeType替换为该例程的相应本地类型。

Get<PrimitiveType>ArrayRegion函数族的数组访问例程

Get<PrimitiveType>ArrayRegion例程 数组类型 本地类型
GetBooleanArrayRegion() jbooleanArray jboolean
GetByteArrayRegion() jbyteArray jbyte
GetCharArrayRegion() jcharArray jchar
GetShortArrayRegion() jshortArray jhort
GetIntArrayRegion() jintArray jint
GetLongArrayRegion() jlongArray jlong
GetFloatArrayRegion() jfloatArray jloat
GetDoubleArrayRegion() jdoubleArray jdouble

参数:

  • env:JNI接口指针。
  • array:Java数组。
  • start:起始索引。
  • len:要复制的元素数量。
  • buf:目标缓冲区。

抛出:

  • 如果区域中的一个索引无效,则抛出ArrayIndexOutOfBoundsException。

注意:自JDK/JRE 1.1版本以来,程序员可以使用Get/Release<primitivetype>ArrayElements函数获取原始类型数组元素的指针。如果虚拟机支持固定,将返回对原始数据的指针;否则,将进行复制。

从JDK/JRE 1.3版本开始引入了新函数,即使虚拟机不支持固定,本地代码也可以获取数组元素的直接指针。

GetPrimitiveArrayCritical、ReleasePrimitiveArrayCritical

void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);           

这两个函数的语义与现有的 Get/Release<primitivetype>ArrayElements 函数非常相似。如果可能,VM 将返回一个指向原始数据的原始指针;否则,将创建一个副本。

然而,这些函数的使用受到重大限制。

在调用 GetPrimitiveArrayCritical 后,本机代码不应长时间运行,直到调用 ReleasePrimitiveArrayCritical。我们必须将这对函数内的代码视为运行在“临界区域”中。在临界区域中,本机代码不能调用其他 JNI 函数或任何可能导致当前线程阻塞并等待另一个 Java 线程的系统调用。 (例如,当前线程不能在由另一个 Java 线程编写的流上调用 read。)

这些限制使得本机代码更有可能获得未复制的数组版本,即使 VM 不支持固定。例如,当本机代码持有通过 GetPrimitiveArrayCritical 获得的数组指针时,VM 可能会暂时禁用垃圾回收。

多对 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);           

请注意,如果 VM 内部以不同的格式表示数组,则 GetPrimitiveArrayCritical 可能仍会复制数组。因此,我们需要检查其返回值是否为 NULL,以避免可能的内存不足情况。

LINKAGE(GetPrimitiveArrayCritical):

  • JNIEnv 接口函数表中的链接索引 222。

LINKAGE(ReleasePrimitiveArrayCritical):

  • JNIEnv 接口函数表中的链接索引 223。

自 JDK/JRE 1.2 开始。