@cxm-2016
2016-11-15T07:42:52.000000Z
字数 7343
阅读 12009
JNI完全指南
版本:1
作者:陈小默
声明:禁止商业,禁止转载
前一篇:JNI完全指南(三)——引用类型
如果我们希望通过一个类创建一个对象,并且没有或不需要调用非默认的构造方法的时候,可以使用如下方式给对象分配空间。
jobject AllocObject(JNIEnv *env, jclass clazz);
- clazz:类。
- return:返回使用clazz类创建的对象,如果clazz没有默认的构造方法,则返回NULL。
存在异常
- InstantiationException:对象初始化异常,这个类可能是抽象的也可能是接口,或者传入的参数与调用的构造器不匹配。
- OutOfMemoryError
这里创建对象和Java类似,我们需要指定类信息,并且选择合适的构造器传入参数。以下提供了三种创建对象的方式:
第一种方式
jobject NewObject(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
- clazz:类
- methodID:构造器方法ID
- ...:可变参数列表
第二种方式
jobject NewObjectA(JNIEnv *env, jclass clazz,
jmethodID methodID, const jvalue *args);
- args:这里需要传入参数数组
第三种方式
jobject NewObjectV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);
- args:指向变参列表的指针
存在异常
- InstantiationException
- OutOfMemoryError
通过如下方法可以从一个Java对象中获取该对象的Java类信息。
jclass GetObjectClass(JNIEnv *env, jobject obj);
通过如下方法可以得到当前对象的引用类型是全局引用、局部引用还是弱全局引用。
jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);
- return:当前对象的引用类型。这些类型在
jni.h文件中的定义如下所示。
typedef enum jobjectRefType {JNIInvalidRefType = 0,//无效引用JNILocalRefType = 1,//局部引用JNIGlobalRefType = 2,//全局引用JNIWeakGlobalRefType = 3//弱全局引用} jobjectRefType;
在Java中我们使用instanceof来判断一个对象是否是一个类的实例,在JNI中,需要通过如下方法来进行实例运算。
jboolean IsInstanceOf(JNIEnv *env, jobject obj,
jclass clazz);
在Java中使用==可以判断两个引用是否指向同一个对象,在JNI中使用下列方法可以进行相同的判断,无论是全局引用,局部引用还是弱全局引用。
jboolean IsSameObject(JNIEnv *env, jobject ref1,
jobject ref2);
当我们拥有了一个Java对象,那么在JNI中应该如何操作这个对象中的属性呢?一般而言,在JNI中如果需要操作一个属性,一般需要先获取到该属性在JVM中的唯一标识ID,然后再通过相应的Get和Set方法去操作属性。
通过如下方法我们可以获取到属性的唯一标识符ID
jfieldID GetFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
- name:使用UTF-8编码的属性名
- sig:使用UTF-8编码的属性类型签名
- return:属性ID
存在异常
- NoSuchFieldError
- ExceptionInInitializerError
- OutOfMemoryError
当我们获取到属性的ID的时候,就可以拿到属性的值了。在JNI中,不同类型的属性有不同的方法获取属性值,如下表所示。
| 获取属性值得函数名 | 返回值类型 |
|---|---|
| GetObjectField | jobject |
| GetBooleanField | jboolean |
| GetByteField | jbyte |
| GetCharField | jchar |
| GetShortField | jshort |
| GetIntField | jint |
| GetLongField | jlong |
| GetFloatField | jfloat |
| GetDoubleField | jdouble |
其参数列表如下:
- obj:一个Java对象(不能为空)
- fieldID:属性ID
设置属性的方法根据属性的类型而定,具体方法名和如下表所示:
| 设置属性值的函数名 | 参数类型 |
|---|---|
| SetObjectField | jobject |
| SetBooleanField | jboolean |
| SetByteField | jbyte |
| SetCharField | jchar |
| SetShortField | jshort |
| SetIntField | jint |
| SetLongField | jlong |
| SetFloatField | jfloat |
| SetDoubleField | jdouble |
参数列表:
- obj:Java对象
- fieldID:属性ID
- value:上表中对应于函数名的参数类型的值。
对于调用实例成员方法,和2.7访问属性的过程类似,需要事先获取这个方法的ID,然后根据这个ID来进行相应的操作。
jmethodID GetMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
- clazz:Java类对象
- name:UTF-8编码的方法名
- sig:UTF-8编码的方法签名
- return:实例方法ID。没有此方法时返回NULL。
存在异常
- NoSuchMethodError
- ExceptionInInitializerError
- OutOfMemoryError
在JNI中,根据不同的参数类型和返回值类型需要调用不同的方法。对于传入的参数类型不同我们需要在方法名的后面使用不同的后缀来标识。
NativeType Call
<type>Method(JNIEnv *env, jobject obj,
jmethodID methodID, ...);NativeType Call
<type>MethodA(JNIEnv *env, jobject obj,
jmethodID methodID, const jvalue *args);NativeType Call
<type>MethodV(JNIEnv *env, jobject obj,
jmethodID methodID, va_list args);
- obj:Java对象
- methodID:实例方法ID
- ...:变参列表
- jvalue *args:参数数组
- va_list args:指向变参列表的指针
所有函数名及其返回值类型如下表所示:
| 函数名 | 返回值类型 |
|---|---|
| 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 |
在C++中,让一个方法具有多态属性需要显式的声明virtual关键字,而在Java中,所有的方法默认都是virtual的。如果对这些概念仍有疑问,可以参考C++:多态公有继承。那么当我们需要以非虚方式调用方法时,JNI提供了以下方法。
方式一:
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);参数列表含义与2.8.2相同,以下列出完整的函数名与返回值表
| 函数名 | 返回值类型 |
|---|---|
| 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 |
在Java中通过类就能够访问静态属性,那么与2.7中访问成员属性相比,这里的差别就不需要通过一个Java对象就能对静态属性进行访问。
通过以下方法能够获取一个静态属性的ID。
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);产生异常
我们先来看一下获取静态属性的一系列函数的名称。完整名称与返回值的对应关系如后表所示。
NativeType GetStatic
<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID);
| 函数名称 | 返回值类型 |
|---|---|
| GetStaticObjectField | jobject |
| GetStaticBooleanField | jboolean |
| GetStaticByteField | jbyte |
| GetStaticCharField | jchar |
| GetStaticShortField | jshort |
| GetStaticIntField | jint |
| GetStaticLongField | jlong |
| GetStaticFloatField | jfloat |
| GetStaticDoubleField | jdouble |
设置静态属性值得函数可表示如下,其函数名称与参数类型的具体关系如后表所示。
void SetStatic
<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID, NativeType value);
| 函数名 | 参数类型 |
|---|---|
| SetStaticObjectField | jobject |
| SetStaticBooleanField | jboolean |
| SetStaticByteField | jbyte |
| SetStaticCharField | jchar |
| SetStaticShortField | jshort |
| SetStaticIntField | jint |
| SetStaticLongField | jlong |
| SetStaticFloatField | jfloat |
| SetStaticDoubleField | jdouble |
通过以下方法获取静态方法的ID
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
调用静态方法的方式有如下三种
第一种
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);
- ...:变参列表
- jvalue *args:Java值数组
- va_list args:指向变参列表的指针
函数名与返回值类型的关系如下表所示:
| 函数名 | 返回参数类型 |
|---|---|
| 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 |