JNI 函数(三)对象操作
(一)、直接创建一个 Java 对象
函数原型:jobject AllocObject(JNIEnv *env, jclass clazz);
不借助任何构造函数的情况下分配一个新的 Java 对象,返回对象的一个引用。
参数:
env:JNI 接口指针
clazz::Java 类对象
返回:
返回一个 Java 对象,如果该对象无法被创建,则返回 NULL。
异常:
如果该类是接口或者是抽象类,则抛出 InstantiationException
如果是内存溢出,则抛出 OutOfMemoryError
(二)、根据某个构造函数来创建 Java 对象
函数原型:jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
函数原型:jobject NewObjectA(JNIEnv *env, jclass clazz,jmethodID methodID, const jvalue *args);
函数原型:jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
构造一个新的 Java 对象,methodID 表明需要调用一个构造函数。这个 ID 必须通过调用 GetMethodID() 获得,GetMethodID() 为函数名,void(V) 为返回值。clazz 参数不能指定一个数组类
NewObject:需要把所有构造函数的入参,放在参数 methodID 之后。NewObject() 接受这些参数并将它们传递给需要被调用的 Java 的构造函数
NewObjectA:在methodID 后面,放了一个类型为 jvalue 的参数数组——args,该数组存放着所有需要传递给构造函数的参数。NewObjectA() 接收到这个数组中的所有参数,并且按照顺序将它们传递给需要调用的 Java 方法。
NewObjectV:在methodID 后面,放了一个类型为 va_lis t的 args,参数存放着所有需要传递给构造函数的参数。NewObjectv() 接收到所有的参数,并且按照顺序将它们传递给需要调用的 Java 方法。
参数:
env:JNI 接口指针
clazz::Java 类
methodID:构造函数的方法 ID
附加参数:
NewObject 的附加参数:arguments 是构造函数的参数
NewObjectA 的附加参数:args 是构造函数的参数数组
NewObjectV 的附加参数:args 是构造函数的参数 list
返回:
Java 对象,如果无法创建该对象,则返回 NULL
异常:
如果传入的类是接口或者抽象类,则抛出 InstantiationException
如果内存溢出,则抛出 OutOfMemoryError
所有的异常都是通过构造函数抛出
(三)、获取某个对象的“类”
函数原型:jclass GetObjectClass(JNIEnv *env, object obj);
返回 obj 对应的类
参数:
env:JNI 接口指针
obj:Java 对象,不能为 NULL
返回:
返回一 个Java “类” 对象
(四)、获取某个对象的“类型”
函数原型:jobjectRefType GetObjectRefType(JNIEnv *env, jobject obj);
返回 obj 参数所以指向对象的类型,参数 obj 可以是局部变量,全局变量或者若全局引用。
参数:
env:JNI 接口指针
obj:局部、全局或弱全局引用
返回:
JNIInvalidRefType=0:代表 obj 参数不是有效的引用类型
JNILocalRefType=1:代表 obj 参数是局部变量类型
JNIGlobalRefType=2:代表 obj 参数是全局变量类型
JNIWeakGlobalRefType=3:代表 obj 参数是弱全局有效引用
无效的引用就是没有引用的引用。也就是说,obj 的指针没有指向内存中创建函数时候的地址,或者已经从 JNI 函数中返回了。所以说 NULL 就是无效的引用。并且 GetObjectRefType(env, NULL) 将返回类型是 JNIInvalidRefType。但是空引用返回的不是 JNIInvalidRefType,而是它被创建时候的引用类型。
PS:不能在引用在删除的时候,调用该函数
(五)、判断某个对象是否是某个“类”的子类
函数原型:jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
测试 obj 是否是 clazz 的一个实例
参数:
env:JNI 接口指针
obj:一个 Java 对象
clazz:一个 Java 的类
返回:
如果 obj 是 clazz 的实例,则返回 JNI_TRUE;否则则返回 JNI_FALSE;一个空对象可以是任何类的实例。
(六)、判断两个引用是否指向同一个引用
函数原型:jboolean IsSampleObject(JNIEnv *env, jobject ref1, jobject ref2);
判断两个引用是否指向同一个对象
参数:
env:JNI 接口指针
ref1:Java 对象
ref2:Java 对象
返回:
如果同一个类对象,返回 JNI_TRUE;否则,返回 JNI_FALSE;
(七)、返回属性id
函数原型:jfieldID GetFieldID(JNIEnv *env,jclass clazz,const char *name,const char *sig);
获取某个类的非静态属性 id。通过方法属性名以及属性的签名(也就是属性的类型),来确定对应的是哪个属性。通过检索这个属性 ID,我们就可以调用 Get <type>Field 和 Set <type>Field了,就是我们常用的 get 和 set 方法
参数:
env:JNI 接口指针
clazz:一个 Java 类对象
name:以 "0" 结尾的,而且字符类型是 "utf-8" 的属性名称
sig:以 "0" 结尾的,而且字符类型是 "utf-8" 的属性签名
返回
属性对应 ID,如果操作失败,则返回 NULL
异常:
如果找不到指定的属性,则抛出 NoSuchFieldError
如果类初始化失败,则抛出 ExceptionInitializerError
如果内存不足了,则抛出 OutOfMemoryError
PS:GetFieldID() 可能会导致还未初始化的类开始初始化,同时在获取数组的长度不能使用 GetFieldID(),而应该使用 GetArrayLength()。
(八)、返回属性id系列
通用函数类型:NativeType Get[type]Field(JNIEnv *env, jobject obj, jfieldID fieldID);
返回某个类的非静态属性的值,这是一组函数的简称,具体如下:
- jobject GetObjectField (JNIEnv *env, jobject obj, jfieldID fielD)
- jboolean GetBooleanField (JNIEnv *env, jobject obj, jfieldID fielD)
- jbyte GetByteField (JNIEnv *env, jobject obj, jfieldID fielD)
- jchar GetCharField (JNIEnv *env, jobject obj, jfieldID fielD)
- jshort GetShortField (JNIEnv *env, jobject obj, jfieldID fielD)
- jint GetIntField (JNIEnv *env, jobject obj, jfieldID fielD)
- jlong GetLongField (JNIEnv *env, jobject obj, jfieldID fielD)
- jfloat GetFloatField (JNIEnv *env, jobject obj, jfieldID fielD)
- jdouble GetDoubleField (JNIEnv *env, jobject obj, jfieldID fielD)
参数:
env:JNI 接口指针
obj:Java 对象,不能为空
fieldID:有效的 fieldID
返回:
对应属性的值
(九)、设置属性id系列
通用函数类型:void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value)
设置某个类的的非静态属性的值。其中具体哪个属性通过 GetFieldID() 来确定哪个属性。这是一组函数的简称,具体如下:
- void SetObjectField (jobject)
- void SetBooleanField (jboolean)
- void SetByteField (jbyte)
- void SetCharField (jchar)
- void SetShortField (jshort)
- void SetIntField (jint)
- void SetLongField (jlong)
- void SetFloatField (jfloat)
- void SetDoubleField (jdouble)
参数:
env:JNI 接口指针
obj:Java 对象,不能为空
fieldID:有效的属性 ID
value:属性的新值
返回值:
void
(十)、获取某个类的某个方法id
函数原型:jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char*name, const char* sig);
返回某个类或者接口的方法 ID,该方法可以是被定义在 clazz 的父类中,然后被 clazz 继承。我们是根据方法的名字以及签名来确定一个方法的。
PS:GetMethodID()会造成还未初始化的类,进行初始化
如果想获取构造函数的 ID,请提供 init 作为方法名称,并将 void(V) 作为返回类型
参数:
env:JNI 接口指针
clazz:Java 类对象
name:以 0 结尾的,并且是 "utf-8" 的字符串的方法名称
sig:以 0 结尾的,并且是 "utf-8" 的字符串的方法签名
返回:
返回一个方法 ID,没有找到指定的方法,则返回 NULL
异常:
如果找不到指定的方法,则抛出 NoSuchMethodError
如果累初始化失败,则抛出 ExceptionInInitializerError
如果内存不够,则抛出 OutOfMemoryError
(十一)、调用Java实例的某个非静态方法“系列”
通用函数类型:NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
通用函数类型:NativeType Call<type>MethodA(JNIEnv *env, jobjct obj, jmethodID methodID, const jvalue *args);
通用函数类型:NativeType Call<type>MethodV(JNEnv *env, jobject obj, jmethodID methodID, va_list args);
这一些列都是在 native 中调用 Java 对象的某个非静态方法,它们的不同点在于传参不同。是根据方法 ID 来指定对应的 Java 对象的某个方法。methodID 参数需要调用 GetMethodID() 获取。
PS:当需要调用某个"private"函数或者构造函数时,这个methodID必须是obj类的方法,不能是它的父类的方法。
下面我们来看下他们的不同点
CallMethod:需要把方法的入参放在参数 methodID 后面。CallMethod() 其实把这些参数传递给需要调用的 Java 方法。
CallMethodA:在 methodID 后面,有一个类型为 jvalue 的 args 数组,该数组存放所有需要传递给构造函数的参数。CallMethodA() 收到这个数组中的参数,是按照顺序将他们传递给对应的 Java 方法。
CallMethodV:在 methodID 后面,有一个类型 va_list 的参数 args,它存放着所有需要传递给构造函数的参数。CallMethodV() 接收所有的参数,并且按照顺序将它们传递给需要调用的 Java 方法。
Call<type>Method Routine Name | Native Type |
CallVoidMethod()、CallVoidMethodA()、CallVoidMethodV() | void |
CallObjectMethod()、CallObjectMethodA()、CallObjectMethodV() | jobject |
CallBooleanMethod()、CallBooleanMethodA()、CallBooleanMethodV() | jboolean |
CallByteMethod()、CallByteMethodA()、CallByteMethodV() | jbyte |
CallCharMethod()、CallCharMethodA()、CallCharMethodV() | jchar |
CallShortMethod()、CallShortMethodA()、CallShortMethodV() | jshort |
CallIntMethod()、CallIntMethodA()、CallIntMethodV() | jint |
CallLongMethod()、CallLongMethodA()、CallLongMethodV() | jlong |
CallFloatMethod()、CallFloatMethodA()、CallFloatMethodV() | jfloat |
CallDoubleMethod()、CallDoubleMethodA()、CallDoubleMethodV() | jdouble |
参数:
env:JNI 接口指针
obj:对应的 Java 对象
methodID:某个方法的方法 id
返回:
返回调用 Java 方法对应的结果
异常:
在 Java 方法执行过程中产生的异常。
(十二)、调用某个类的非抽象方法
调用父类中的实例方法,如下系列
CallNonvirtual<type>Method
CallNonvirtual<type>MethodA
CallNonvirtual<type>MethodV
具体如下:
NativeType CallNonvirtual<Type>Method(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ....);
NativeType CallNonvirtual<Type>MethodA(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args);
NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args);
这一系列操作就是根据特定的类,和其方法 ID 来调用 Java 对象的实例的非静态方法,methodID 参数需要调用 GetMethodID() 获取。
CallNonvirtual<Type>Method 和 Call<type>Method 是不同的,其中 CallNonvirtual<Type>Method 是基于"类",而和 Call<type>Method 是基于类的对象。所以说 CallNonvirtual<Type>Method 的入参是 clazz,methodID 必须来源于 obi 的类,而不是它的父类
下面我们来看下他们的不同点
CallNonvirtual<type>Method :需要把方法的入参放在参数 methodID 后面。CallNonvirtual<type>Method() 其实把这些参数传递给需要调用的Java方法。
CallNonvirtual<type>Method:在 methodID 后面,有一个类型为 jvalue 的args数组,该数组存放所有需要传递给构造函数的参数。CallNonvirtual<type>Method() 收到这个数组中的参数,是按照顺序将他们传递给对应的 Java 方法
CallNonvirtual<type>MethodV :在 methodID 后面,有一个类型 va_list 的参数 args,它存放着所有需要传递给构造函数的参数。 CallNonvirtual<type>MethodV() 接收所有的参数,并且按照顺序将它们传递给需要调用的 Java 方法。
将上面这系列方法展开如下:
将上面这系列方法展开如下:
CallNonvirtual<type>Method Routine Name | Native Type |
CallNonvirtualVoidMethod()、CallNonvirtualVoidMethodA()、CallNonvirtualVoidMethodV() | void |
CallNonvirtualObjectMethod()、CallNonvirtualObjectMethodA()、CallNonvirtualObjectMethodV() | jobject |
CallNonvirtualBooleanMethod()、CallNonvirtualBooleanMethodA()、CallNonvirtualBooleanMethodV() | jboolean |
CallNonvirtualByteMethod()、CallNonvirtualByteMethodA()、CallNonvirtualByteMethodV() | jbyte |
CallNonvirtualCharMethod()、CallNonvirtualCharMethodA()、CallNonvirtualCharMethodV() | jchar |
CallNonvirtualShortMethod()、CallNonvirtualShortMethodA()、CallNonvirtualShortMethodV() | jshort |
CallNonvirtualIntMethod()、CallNonvirtualIntMethodA()、CallNonvirtualIntMethodV() | jint |
CallNonvirtualLongMethod()、CallNonvirtualLongMethodA()、CallNonvirtualLongMethodV() | jlong |
CallNonvirtualFloatMethod()、CallNonvirtualFloatMethodA()、CallNonvirtualFloatMethodV() | jfloat |
CallNonvirtualDoubleMethod()、CallNonvirtualDoubleMethodA()、CallNonvirtualDoubleMethodV() | jdouble |
参数:
env:JNI 接口指针
obj:Java 对象
clazz:Java 类
methodID:方法 ID
返回:
调用 Java 方法的结果
抛出异常:
在 Java 方法中执行过程可能产生的异常
(十三)、获取静态(static)属性
函数原型:jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char* name, const char *sig);
获取某个类的某个静态属性 ID,根据属性名以及标签来确定是哪个属性。GetStaticField() 和 SetStaticField() 通过使用属性 ID 来对属性进行操作的。如果这个类还没有初始化,直接调用 GetStaticFieldID() 会引起这个类进行初始化。
参数:
env:JNI 接口指针
clazz:Java 类
name:静态属性的属性名,是一个编码格式 "utf-8" 并且以 0 结尾的字符串。
sig:属性的签名,是一个编码格式 "utf-8" 并且以 0 结尾的字符串。
返回:
返回静态属性 ID,如果指定的静态属性无法找则返回 NULL
异常:
如果指定的静态属性无法找到则抛出 NoSuchFieldError
如果类在初始化失败,则抛出 ExceptionInInitializerError
如果内存不够,则抛出 OutOfMemoryError
(十四)、获取静态(static)属性系列
函数通用类型:NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID);
这个系列返回一个对象的静态属性的值。可以通过 GetStaticFieldID() 来获取静态属性的的 ID,有了这个 ID,我们就可以获取这个对其进行操作了
下面表明了函数名和函数的返回值,所以只需要替换 GetStatic<type>Field 中的类替换为该字段的Java类型或者表中的实际静态字段存取器。并将 NativeType 替换为相应的本地类型
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 类
field:静态属性 ID
返回:
返回静态属性
(十五)、设置静态属性系列
函数通用原型:void SetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value);
这个系列是设置类的静态属性的值。可以通过 GetStaticFieldID() 来获取静态属性的 ID。
下面详细介绍了函数名和其值,你可以通过 SetStatic<type> 并传入的 NativeType 来设置 Java 中的静态属性。
SetStatic<type>Field Routine Name | NativeType |
SetStaticObjectField() | jobject |
SetStaticBooleanField() | jboolean |
SetStaticByteField() | jbyte |
SetStaticCharField() | jchar |
SetStaticShortField() | jshort |
SetStaticIntField() | jint |
SetStaticLongField() | jlong |
SetStaticFloatField() | jfloat |
SetStaticDoubleField() | jdouble |
参数:
env:JNI 接口指针
clazz:Java 类
field:静态属性 ID
value:设置的值
(十六)、获取静态函数ID
函数原型:jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char sig);
返回类的静态方法 ID,通过它的方法名以及签名来确定哪个方法。如果这个类还没被初始化,调用 GetStaticMethodID() 将会导致这个类初始化。
参数:
env:JNI 接口指针
clazz:Java 类
name:静态方法的方法名,以 "utf-8" 编码的,并且以 0 结尾的字符串
sig:方法签名,以 "utf-8" 编码的,并且以 0 结尾的字符串
返回:
返回方法 ID,如果操作失败,则返回 NULL
异常:
如果没有找到对应的静态方法,则抛出 NoSuchMethodError
如果类初始化失败,则抛出 ExceptionInInitializerError
如果系统内存不足,则抛出 OutOfMemoryError
(十七)、调用静态函数系列
函数通用原型: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, jmethodID methodid, va_list args);
根据指定的方法 ID,就可以操作 Java 对象的静态方法了。可以通过 GetStaticMethodID() 来获得 methodID。方法的 ID 必须是 clazz的,而不是其父类的方法 ID。
下面就是详细的方法了
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
返回:
返回静态的 Java 方法的调用方法
异常:
在 Java 方法中执行中抛出的异常