掌握JNI

要素:1、 该函数大全是基于C语言方式的,对于C++方式可以直接转换 ,例如,对于生成一个jstring类型的方法转换分别如下:
C编程环境中使用方法为:(*env) ->NewStringUTF(env , "123") ;
C++编程环境中(例如,VC下)则是: env ->NewStringUTF( "123") ; (使用起来更简单)

2、关于下列有些函数中:*isCopy的说明,例如,如下函数:
const char* GetStringUTFChars(JNIEnv*env, jstring string, jboolean *isCopy);

对第三个参数 jboolean *isCopy说明如下:
当从JNI函数GetStringUTFChars函数中返回得到字符串B时,如果B是原始字符串java.lang.String的一份拷贝,
则isCopy被赋值为JNI_TRUE。如果B是和原始字符串指向的是JVM中的同一份数据,则isCopy被赋值为JNI_FALSE。
当isCopy为JNI_FALSE时,本地代码绝不能修改字符串的内容,否则JVM中的原始字符串也会被修改,这会打破Java语言
中字符串不可变的规则。
通常,我们不必关心JVM是否会返回原始字符串的拷贝,只需要为isCopy传递NULL作为参数 。
----以上内容来自 《JNI编程指南》

一、类操作

jclass DefineClass (JNIEnv *env, jobject loader,const jbyte *buf , jsize bufLen);
功能:从原始类数据的缓冲区中加载类。
参数: envJNI 接口指针。
loader分派给所定义的类的类加载器。
buf包含 .class 文件数据的缓冲区。
bufLen缓冲区长度。
返回值:返回 Java 类对象。如果出错则返回NULL。
抛出: ClassFormatError如果类数据指定的类无效。
ClassCircularityError如果类或接口是自身的超类或超接口。
OutOfMemoryError如果系统内存不足。

jclass FindClass (JNIEnv *env, const char *name);
功能: 该函数用于加载本地定义的类。它将搜索由CLASSPATH 环境变量为具有指定名称的类所指定的目录和 zip文件。
参数:envJNI 接口指针。
name类全名(即包名后跟类名,之间由"/"分隔).如果该名称以“[(数组签名字符)打头,则返回一个数组类。
返回值:返回类对象全名。如果找不到该类,则返回 NULL。
抛出:ClassFormatError如果类数据指定的类无效。
ClassCircularityError如果类或接口是自身的超类或超接口。
NoClassDefFoundError如果找不到所请求的类或接口的定义。
OutOfMemoryError如果系统内存不足。

jclass GetObjectClass (JNIEnv *env, jobject obj);
功能:通过对象获取这个类。该函数比较简单,唯一注意的是对象不能为NULL,否则获取的class肯定返回也为NULL。
参数:envJNI 接口指针。
objJava 类对象实例。

jclass GetSuperclass (JNIEnv *env, jclass clazz);
功能:获取父类或者说超类 。 如果 clazz 代表类class而非类 object,则该函数返回由 clazz 所指定的类的超类。 如果 clazz
指定类 object 或代表某个接口,则该函数返回NULL。
参数:envJNI 接口指针。
clazzJava 类对象。
返回值:由 clazz 所代表的类的超类或 NULL。

jboolean IsAssignableFrom (JNIEnv *env, jclass clazz1,jclass clazz2);
功能:确定 clazz1 的对象是否可安全地强制转换为clazz2。
参数:envJNI 接口指针。
clazz1 第一个类参数。
clazz2 第二个类参数。
返回值:下列某个情况为真时返回 JNI_TRUE:
1、 第一及第二个类参数引用同一个 Java 类。
2、 第一个类是第二个类的子类。
3、 第二个类是第一个类的某个接口。



二、异常操作

jintThrow(JNIEnv *env, jthrowable obj);
功能:抛出 java.lang.Throwable 对象。
参数: envJNI 接口指针。
objjava.lang.Throwable 对象。
返回值:成功时返回 0,失败时返回负数。
抛出:java.lang.Throwable 对象 obj。

jint ThrowNew (JNIEnv *env ,jclass clazz,const char *message);
功能:利用指定类的消息(由 message 指定)构造异常对象并抛出该异常。
参数: envJNI 接口指针。
clazzjava.lang.Throwable 的子类。
message用于构造java.lang.Throwable对象的消息。
返回值: 成功时返回 0,失败时返回负数。
抛出:新构造的 java.lang.Throwable 对象。

jthrowable ExceptionOccurred (JNIEnv *env);
功能:确定是否某个异常正被抛出。在平台相关代码调用 ExceptionClear() 或 Java 代码处理该异常前,异常将始终保持
抛出状态。
参数:envJNI 接口指针。
返回值: 返回正被抛出的异常对象,如果当前无异常被抛出,则返回NULL。

void ExceptionDescribe (JNIEnv *env);
功能:将异常及堆栈的回溯输出到系统错误报告信道(例如 stderr)。该例程可便利调试操作。
参数:envJNI 接口指针。

void ExceptionClear (JNIEnv *env);
功能:清除当前抛出的任何异常。如果当前无异常,则此例程不产生任何效果。
参数: envJNI 接口指针。

void FatalError (JNIEnv *env, const char *msg);
功能:抛出致命错误并且不希望虚拟机进行修复。该函数无返回值。
参数:envJNI 接口指针。
msg错误消息。




三、全局及局部引用

jobject NewGlobalRef (JNIEnv *env, jobject obj);
功能:创建 obj 参数所引用对象的新全局引用。obj 参数既可以是全局引用,也可以是局部引用。全局引用通过调用
DeleteGlobalRef() 来显式撤消。
参数:envJNI 接口指针。
obj全局或局部引用。
返回值: 返回全局引用。如果系统内存不足则返回 NULL。

void DeleteGlobalRef (JNIEnv *env, jobject globalRef);
功能: 删除 globalRef 所指向的全局引用。
参数: envJNI 接口指针。
globalRef全局引用。

voidDeleteLocalRef (JNIEnv *env, jobject localRef);
功能: 删除 localRef所指向的局部引用。
参数: envJNI 接口指针。
localRef局部引用。




四、对象操作

jobject AllocObject (JNIEnv *env, jclass clazz);
功能:分配新 Java 对象而不调用该对象的任何构造函数。返回该对象的引用。clazz 参数务必不要引用数组类。
参数: envJNI 接口指针。
clazzJava 类对象。
返回值: 返回 Java 对象。如果无法构造该对象,则返回NULL。
抛出: InstantiationException:如果该类为一个接口或抽象类。
OutOfMemoryError:如果系统内存不足。

jobject NewObject (JNIEnv *env ,jclass clazz,jmethodID methodID, ...); //参数附加在函数后面
jobject NewObjectA (JNIEnv *env , jclassclazz,jmethodID methodID, jvalue *args); //参数以指针形式附加
jobjec tNewObjectV (JNIEnv *env , jclassclazz,jmethodID methodID, va_list args); //参数以"链表"形式附加


功能:构造新 Java 对象。方法 ID指示应调用的构造函数方法。注意:该 ID特指该类class的构造函数ID , 必须通过调用
GetMethodID() 获得,且调用时的方法名必须为 ,而返回类型必须为 void (V)。clazz参数务必不要引用数组类。
参数:envJNI 接口指针。
clazzJava 类对象。
methodID 构造函数的方法 ID。
NewObject 的其它参数:传给构造函数的参数,可以为空 。
NewObjectA 的其它参数: args:传给构造函数的参数数组。
NewObjectV 的其它参数: args:传给构造函数的参数 va_list。

返回值: 返回 Java 对象,如果无法构造该对象,则返回NULL。
抛出:InstantiationException如果该类为接口或抽象类。
OutOfMemoryError如果系统内存不足。
构造函数抛出的任何异常。

jclass GetObjectClass (JNIEnv *env, jobject obj);
功能:返回对象的类。
参数: envJNI 接口指针。
objJava 对象(不能为 NULL)。
返回值: 返回 Java 类对象。

jboolean IsInstanceOf (JNIEnv *env, jobject obj, jclass clazz);
功能:测试对象是否为某个类的实例。
参数:envJNI 接口指针。
objJava 对象。
clazz Java 类对象。
返回值:如果可将 obj 强制转换为 clazz,则返回 JNI_TRUE。否则返回 JNI_FALSE。NULL 对象可强制转换为任何类。

jbooleanIsSameObject (JNIEnv *env, jobjectref1, jobject ref2);
功能:测试两个引用是否引用同一 Java 对象。
参数:envJNI 接口指针。
ref1Java 对象。
ref2Java 对象。
返回值: 如果 ref1 和 ref2 引用同一 Java 对象或均为 NULL,则返回 JNI_TRUE。否则返回 JNI_FALSE。




五、 字符串操作

jstringNewString (JNIEnv *env, const jchar *unicodeChars,jsize len);
功能:利用 Unicode 字符数组构造新的 java.lang.String 对象。
参数:env:JNI 接口指针。
unicodeChars:指向 Unicode 字符串的指针。
len:Unicode 字符串的长度。
返回值: Java 字符串对象。如果无法构造该字符串,则为NULL。
抛出: OutOfMemoryError:如果系统内存不足。

jsizeGetStringLength (JNIEnv *env, jstring string);
功能:返回 Java 字符串的长度(Unicode 字符数)。
参数:env:JNI 接口指针。
string:Java 字符串对象。
返回值: Java 字符串的长度。

constjchar *GetStringChars (JNIEnv*env, jstring string,jboolean *isCopy);
功能:返回指向字符串的 Unicode 字符数组的指针。该指针在调用 ReleaseStringchars() 前一直有效。
如果 isCopy 非空,则在复制完成后将 *isCopy 设为 JNI_TRUE。如果没有复制,则设为JNI_FALSE。
参数:env:JNI 接口指针。
string:Java 字符串对象。
isCopy:指向布尔值的指针。
返回值:指向 Unicode 字符串的指针,如果操作失败,则返回NULL。

voidReleaseStringChars (JNIEnv *env, jstring string,const jchar *chars);
功能:通知虚拟机平台相关代码无需再访问 chars。参数chars 是一个指针,可通过 GetStringChars() 从 string 获得。
参数: env:JNI 接口指针。
string:Java 字符串对象。
chars:指向 Unicode 字符串的指针。

jstringNewStringUTF (JNIEnv *env, const char *bytes);
功能:利用 UTF-8 字符数组构造新 java.lang.String 对象。
参数: env:JNI 接口指针。如果无法构造该字符串,则为 NULL。
bytes:指向 UTF-8 字符串的指针。
返回值:Java 字符串对象。如果无法构造该字符串,则为NULL。
抛出:OutOfMemoryError:如果系统内存不足。

jsizeGetStringUTFLength (JNIEnv *env, jstring string);
功能:以字节为单位返回字符串的 UTF-8 长度。
参数:env:JNI 接口指针。
string:Java 字符串对象。
返回值:返回字符串的 UTF-8


const char* GetStringUTFChars (JNIEnv*env, jstring string, jboolean *isCopy);
功能:返回指向字符串的 UTF-8 字符数组的指针。该数组在被ReleaseStringUTFChars() 释放前将一直有效。如果 isCopy
不是 NULL,*isCopy 在复制完成后即被设为 JNI_TRUE。如果未复制,则设为 JNI_FALSE。
参数:env:JNI 接口指针。
string:Java 字符串对象。
isCopy:指向布尔值的指针。
返回值:指向 UTF-8 字符串的指针。如果操作失败,则为 NULL。

voidReleaseStringUTFChars (JNIEnv *env, jstring string,const char *utf);
功能:通知虚拟机平台相关代码无需再访问 utf。utf 参数是一个指针,可利用 GetStringUTFChars() 获得。
参数:env:JNI 接口指针。
string:Java 字符串对象。
utf:指向 UTF-8 字符串的指针。




六、数组操作


jsize GetArrayLength (JNIEnv *env, jarray array);
功能:返回数组中的元素数。
参数:env:JNI 接口指针。
array:Java 数组对象。
返回值: 数组的长度。

jarray NewObjectArray (JNIEnv *env, jsize length,jclass elementClass, jobject initialElement);
功能:构造新的数组,它将保存类 elementClass 中的对象。所有元素初始值均设为 initialElement。
参数: env:JNI 接口指针。
length:数组大小。
elementClass:数组元素类。
initialElement:初始值。可以为NULL 。
返回值:Java 数组对象。如果无法构造数组,则为 NULL。
抛出:OutOfMemoryError:如果系统内存不足。

说明: 使用该函数时,为了便于易操作性,我们一般可以用jobjectArray数组类型或得返回值,例如:
jobjectArray objArray = env->NewObjectArray ( );
//操作该对象
env->GetObjectArrayElement (objArray, 0); //获得该object数组在索引0处的值 ,(可以强制转换类型).


jobjectGetObjectArrayElement (JNIEnv *env,jobjectArray array, jsize index);
功能:返回 Object 数组的元素。
参数:env:JNI 接口指针。
array:Java 数组。
index:数组下标。
返回值: Java 对象。
抛出: ArrayIndexOutOfBoundsException:如果 index 不是数组中的有效下标。

voidSetObjectArrayElement (JNIEnv *env, jobjectArray array,jsize index, jobject value);
功能:设置 Object 数组的元素。
参数:env:JNI 接口指针。
array:Java 数组。
index:数组下标。
value:新值。
抛出: ArrayIndexOutOfBoundsException:如果 index 不是数组中的有效下标。
ArrayStoreException:如果 value 的类不是数组元素类的子类。


NewArray方法类型

NativeType NewArray (JNIEnv *env, ArrayType array, jboolean*isCopy);

说明: 用于构造新基本类型数组对象的一系列操作。下表说明了特定的基本类型数组构造函数。用户应把
NewArray 替换为某个实际的基本类型数组构造函数例程名(见下表),然后将 ArrayType替换为
 该例程相应的数组类型。
参数:env : JNI 接口指针。
length:数组长度。
返回值:Java 数组。如果无法构造该数组,则为 NULL。

NewArray 方法组数组类型
NewBooleanArray()jbooleanArray
NewByteArray()jbyteArray
NewCharArray()jcharArray
NewShortArray()jshortArray
NewIntArray()jintArray
NewLongArray()jlongArray
NewFloatArray()jfloatArray
NewDoubleArray()jdoubleArray


GetArrayElements 方法类型

NativeType *GetArrayElements (JNIEnv *env, ArrayType array, jboolean*isCopy);
说明:一组返回基本类型数组体的函数。结果在调用相应的 ReleaseArrayElements()函数前将一直有效。
由于返回的数组可能是 Java 数组的副本,因此对返回数组的更改不必在基本类型数组中反映出来,直到调用了
ReleaseArrayElements()。 如果 isCopy 不是 NULL,*isCopy 在复制完成后即被设为 JNI_TRUE。如果
未复制,则设为 JNI_FALSE。
使用说明:
将 GetArrayElements 替换为表中某个实际的基本类型元素访问器例程名。
将 ArrayType 替换为对应的数组类型。
将 NativeType 替换为该例程对应的本地类型。
参数:env:JNI 接口指针。
array:Java 字符串对象。
isCopy:指向布尔值的指针。
返回值:返回指向数组元素的指针,如果操作失败,则为 NULL。

不管布尔数组在 Java 虚拟机中如何表示,GetBooleanArrayElements() 将始终返回一个 jbooleans 类型的指针,其中每一
 字节代表一个元素(开包表示)。内存中将确保所有其它类型。

GetArrayElements 例程数组类型本地类型
GetBooleanArrayElements()jbooleanArrayjboolean
GetByteArrayElements()jbyteArrayjbyte
GetCharArrayElements()jcharArrayjchar
GetShortArrayElements()jshortArrayjshort
GetIntArrayElements()jintArrayjint
GetLongArrayElements()jlongArrayjlong
GetFloatArrayElements()jfloatArrayjfloat
GetDoubleArrayElements()jdoubleArrayjdouble


ReleaseArrayElements 方法类型

voidReleaseArrayElements (JNIEnv *env, ArrayType array, NativeType *elems,jint mode);
功能:通知虚拟机平台相关代码无需再访问 elems 的一组函数。elems 参数是一个通过使用对应的
GetArrayElements() 函数由 array 导出的指针。必要时,该函数将把对 elems 的修改复制回基本
类型数组。mode参数将提供有关如何释放数组缓冲区的信息。如果elems 不是 array 中数组元素的副本,mode将无效。
否则,mode 将具有下表所述的功能:
模式动作
0复制回内容并释放elems 缓冲区
JNI_COMMIT复制回内容但不释放elems 缓冲区
JNI_ABORT释放缓冲区但不复制回变化
多数情况下,编程人员将把“0”传给 mode 参数以确保固定的数组和复制的数组保持一致。其它选项可以使编程人员进一步
控制内存管理,但使用时务必慎重。
使用说明:
将 ArrayType 替换为对应的数组类型。
将 NativeType 替换为该例程对应的本地类型。

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

ReleaseArrayElements 方法组数组类型本地类型
ReleaseBooleanArrayElements()jbooleanArrayjboolean
ReleaseByteArrayElements()jbyteArrayjbyte
ReleaseCharArrayElements()jcharArrayjchar
ReleaseShortArrayElements()jshortArrayjshort
ReleaseIntArrayElements()jintArrayjint
ReleaseLongArrayElements()jlongArrayjlong
ReleaseFloatArrayElements()jfloatArrayjfloat
ReleaseDoubleArrayElements()jdoubleArrayjdouble


GetArrayRegion 方法类型

voidGetArrayRegion (JNIEnv *env, ArrayType array,jsize start, jsize len, NativeType *buf);
功能:将基本类型数组某一区域复制到缓冲区中的一组函数。
使用说明:
将 GetArrayRegion 替换为下表的某个实际基本类型元素访问器例程名。
将 ArrayType 替换为对应的数组类型。
将 NativeType 替换为该例程对应的本地类型。
参数:env:JNI 接口指针。
array:Java 指针。
start:起始下标。
len:要复制的元素数。
buf:目的缓冲区。
抛出:ArrayIndexOutOfBoundsException:如果区域中的某个下标无效。

方法族如下:
GetArrayRegion方法数组类型本地类型
GetBooleanArrayRegion()jbooleanArrayjboolean
GetByteArrayRegion()jbyteArrayjbyte
GetCharArrayRegion()jcharArrayjchar
GetShortArrayRegion()jshortArrayjhort
GetIntArrayRegion()jintArrayjint
GetLongArrayRegion()jlongArrayjlong
GetFloatArrayRegion()jfloatArrayjloat
GetDoubleArrayRegion()jdoubleArrayjdouble


SetArrayRegion 方法类型

voidSetArrayRegion (JNIEnv *env, ArrayType array,jsize start, jsize len, NativeType *buf);
功能:将基本类型数组的某一区域从缓冲区中复制回来的一组函数。
使用说明:将 SetArrayRegion 替换为表中的实际基本类型元素访问器例程名。
将 ArrayType 替换为对应的数组类型。
将 NativeType 替换为该例程对应的本地类型。
参数: env:JNI 接口指针。
array: Java 数组。
start:起始下标。
len:要复制的元素数。
buf:源缓冲区。
抛出: ArrayIndexOutOfBoundsException:如果区域中的某个下标无效。

SetArrayRegion 方法族数组类型本地类型
SetBooleanArrayRegion()jbooleanArrayjboolean
SetByteArrayRegion()jbyteArrayjbyte
SetCharArrayRegion()jcharArrayjchar
SetShortArrayRegion()jshortArrayjshort
SetIntArrayRegion()jintArrayjint
SetLongArrayRegion()jlongArrayjlong
SetFloatArrayRegion()jfloatArrayjfloat
SetDoubleArrayRegion()jdoubleArrayjdouble





六、访问对象的属性和方法

1、实例属性的访问

jfieldIDGetFieldID (JNIEnv *env, jclass clazz, const char *name, const char *sig);
功能:返回类的实例(非静态)域的属性 ID。该域由其名称及签名指定。访问器函数的GetField 及 SetField
系列使用域 ID 检索对象域。GetFieldID() 不能用于获取数组的长度域。应使用GetArrayLength()。
参数:env:JNI 接口指针。
clazz:Java 类对象。
name: 该属性的Name名称
sig:该属性的域签名。
返回值:属性ID。如果操作失败,则返回NULL。
抛出: NoSuchFieldError:如果找不到指定的域。
ExceptionInInitializerError:如果由于异常而导致类初始化程序失败。
OutOfMemoryError:如果系统内存不足。

GetField 例程

NativeTypeGetField (JNIEnv*env, jobject obj, jfieldID fieldID);
功能:该访问器例程系列返回对象的实例(非静态)域的值。要访问的域由通过调用GetFieldID() 而得到的域 ID 指定。
参数:env:JNI 接口指针。
obj:Java 对象(不能为 NULL)。
fieldID:有效的域 ID。
返回值:属性的内容。
GetField 例程名本地类型
GetObjectField()jobject
GetBooleanField()jboolean
GetByteField()jbyte
GetCharField()jchar
GetShortField()jshort
GetIntField()jint
GetLongField()jlong
GetFloatField()jfloat
GetDoubleField()jdouble

SetField 方法族

voidSetField (JNIEnv *env, jobject obj, jfieldID fieldID,NativeType value);
功能: 该访问器例程系列设置对象的实例(非静态)属性的值。要访问的属性由通过调用SetFieldID() 而得到的属性 ID指定。

参数:env:JNI 接口指针。
obj:Java 对象(不能为 NULL)。
fieldID:有效的域 ID。
value:域的新值。
方法族 如下:
SetField 方法族本地类型
SetObjectField()jobject
SetBooleanField()jboolean
SetByteField()jbyte
SetCharField()jchar
SetShortField()jshort
SetIntField()jint
SetLongField()jlong
SetFloatField()jfloat
SetDoubleField()jdouble

2、静态属性的访问 :也存在相同的方法,

jfieldIDGetStaticFieldID (JNIEnv *env,jclass clazz, const char *name, const char *sig);
NativeTypeGetStaticField (JNIEnv*env,jclass classzz , jfieldID fieldID);
voidSetStaticField (JNIEnv *env,jclassclasszz, jfieldID fieldID,NativeType value);

它们与实例属性的唯一区别在于第二个参数jclass classzz代表的是类引用,而不是类实例。

3、调用实例方法

jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
功能:返回类或接口实例(非静态)方法的方法 ID。方法可在某个 clazz 的超类中定义,也可从 clazz 继承。该方法由其名称
和签名决定。 GetMethodID() 可使未初始化的类初始化。要获得构造函数的方法 ID,应将 作为方法名,同时将
void (V) 作为返回类型。
参数:env:JNI 接口指针。
clazz:Java 类对象。
name:方法名。
sig:方法的签名。
返回值: 方法 ID,如果找不到指定的方法,则为 NULL。
抛出:NoSuchMethodError:如果找不到指定方法。
ExceptionInInitializerError:如果由于异常而导致类初始化程序失败。
OutOfMemoryError:如果系统内存不足。

CallMethod 例程、CallMethodA 例程、CallMethodV 例程

NativeType CallMethod (JNIEnv*en v,jobject obj , jmethodID methodID, ...); //参数附加在函数后面,
NativeType CallMethodA (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args); //参数以指针形式附加
NativeType CallMethodV (JNIEnv *env, jobject obj,jmethodID methodID, va_list args); //参数以"链表"形式附加

说明:这三个操作的方法用于从本地方法调用Java 实例方法。它们的差别仅在于向其所调用的方法传递参数时所用的机制。
这三个操作将根据所指定的方法 ID 调用 Java 对象的实例(非静态)方法。参数 methodID 必须通过调用 GetMethodID()
来获得。当这些函数用于调用私有方法和构造函数时,方法 ID 必须从obj 的真实类派生而来,而不应从其某个超类派生。
当然,附加参数可以为空 。
参数:env:JNI 接口指针。
obj:Java 对象。
methodID:方法 ID。
返回值: 返回调用 Java 方法的结果。
抛出:执行 Java 方法时抛出的异常。



下表根据结果类型说明了各个方法类型。用户应将CallMethod 中的 type 替换为所调用方法的Java 类型(或使用表
中的实际方法名),同时将 NativeType 替换为该方法相应的本地类型。省略掉了其他两种类型。


Java层返回值方法族本地返回类型NativeType


返回值为void :CallVoidMethod( )A / V(无)
返回值为引用类型:CallObjectMethod( )jobect
返回值为boolean :CallBooleanMethod ( )jboolean
返回值为byte :CallByteMethod( )jbyte
返回值为char:CallCharMethod( )jchar
返回值为shortCallShortMethod( )jshort
返回值为int:CallIntMethod( )jint
返回值为long:CallLongMethod()jlong
返回值为float :CallFloatMethod()jfloat
返回值为double:CallDoubleMethod()jdouble


4、调用静态方法:也存在如下方法群,

jfieldIDGetStaticMethodID (JNIEnv *env,jclass clazz, const char *name, const char *sig);
NativeTypeCallMethod (JNIEnv*env,jclass classzz , jfieldID fieldID);

它们与于实例方法的唯一区别在于第二个参数jclass classzz代表的是类引用,而不是类实例。



七、注册本地方法

jintRegisterNatives (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,jintnMethods);
功能:向 clazz 参数指定的类注册本地方法。methods 参数将指定 JNINativeMethod 结构的数组,其中包含本地方法的名称、
签名和函数指针。nMethods 参数将指定数组中的本地方法数。JNINativeMethod 结构定义如下所示:
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
函数指针通常必须有下列签名:
ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...);

参数: env:JNI 接口指针。
clazz:Java 类对象。
methods:类中本地方法和具体实现方法的映射指针。
nMethods:类中的本地方法数。
返回值:成功时返回 "0";失败时返回负数。
抛出:NoSuchMethodError:如果找不到指定的方法或方法不是本地方法。

jintUnregisterNatives (JNIEnv *env, jclass clazz);
功能: 取消注册类的本地方法。类将返回到链接或注册了本地方法函数前的状态。该函数不应在常规平台相关代码中使用。
相反,它可以为某些程序提供一种重新加载和重新链接本地库的途径。
参数:env:JNI 接口指针。
clazz:Java 类对象。
返回值: 成功时返回“0”;失败时返回负数。










在Java存在两种数据类型: 基本类型 和 引用类型 ,大家都懂的 。


在JNI的世界里也存在类似的数据类型,与Java比较起来,其范围更具严格性,如下:


1、primitive types ----基本数据类型,如:int、 float 、char等基本类型
2、reference types----引用类型,如:类、实例、数组。

特别需要注意:数组 ------ 不管是对象数组还是基本类型数组,都作为reference types存在。

1、primitive types (基本数据类型)映射参见下表:





这些基本数据类型都是可以在Native层直接使用的 。

2、reference types (引用数据类型)映射参见下表


Java类型Native Type描述



注意:
1、引用数据类型则不能直接使用,需要根据JNI函数进行相应的转换后,才能使用
2、多维数组(包括二维数组)都是引用类型,需要使用jobjectArray 类型存取其值 ;
例如:二维整型数组就是指向一位数组的数组,其声明使用方式如下:

[java] view plain copy print ?

  1. //获得一维数组 的类引用,即jintArray类型
  2. jclass intArrayClass = env->FindClass("[I");
  3. //构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion
  4. jobjectArray obejctIntArray=env->NewObjectArray(dimion ,intArrayClass , NULL);
  5. ...//具体操作


另外,关于引用类型的一个继承关系如下,我们可以对具有父子关系的类型进行转换:



类描述符

类描述符是类的完整名称(包名+类名),将原来的 . 分隔符换成 / 分隔符。
例如:在java代码中的java.lang.String类的类描述符就是java/lang/String


其实,在实践中,我发现可以直接用该类型的域描述符取代,也是可以成功的。
例如:jclass intArrCls = env->FindClass("java/lang/String")
等同于jclass intArrCls = env->FindClass("Ljava/lang/String; ")


数组类型的描述符则为,则为:[ + 其类型的域描述符(后文说明)

例如:
int [ ]其描述符为[I
float [ ]其描述符为[F
String [ ]其描述符为[Ljava/lang/String;

域描述符
1、基本类型的描述符已经被定义好了,如下表所示:





2、引用类型的描述符


一般引用类型则为 L + 该类型类描述符 + ; (注意,这儿的分号“;”只得是JNI的一部分,而不是我们汉语中的分段,下同)
例如:String类型的域描述符为 Ljava/lang/String;


对于数组,其为 :[ + 其类型的域描述符 + ;

int[ ]其描述符为[I
float[ ]其描述符为[F
String[ ]其描述符为[Ljava/lang/String;
Object[ ]类型的域描述符为[Ljava/lang/Object;



多维数组则是 n个[ +该类型的域描述符 , N代表的是几维数组。例如:
int[ ][ ] 其描述符为[[I
float[ ][ ] 其描述符为[[F

方法描述符
将参数类型的域描述符按照申明顺序放入一对括号中后跟返回值类型的域描述符,规则如下: (参数的域描述符的叠加)返回
类型描述符。对于,没有返回值的,用V(表示void型)表示。举例如下:


Java层方法JNI函数签名
String test ( )Ljava/lang/String;
int f (int i, Object object)(ILjava/lang/Object; )I
void set (byte[ ] bytes)([B)V


在编程时,如果是利用javah工具的话,这些都不需要我们手动编写对应的类型转换,如果不能用javah工具,就只能手动的
进行类型转换了。
总的来说,JNI是不难的。通过前面的学习 相信你应该有所了解 。今天,我们从几个简单的小例子,来对JNI进行下实战训练。 可都是些小例子,耐心看咯。

主要操作内容,包括如下几个部分:


1、在Native层返回一个字符串
2、从Native层返回一个int型二维数组(int a[ ][ ])
3、从Native层操作Java层的类: 读取/设置类属性
4、在Native层操作Java层的类:读取/设置类属性、回调Java方法
5、从Native层返回一个复杂对象(即一个类咯)
6、在Java层传递复杂对象至Native层
7、从Native层返回Arraylist集合对象


广而告知,这些操作就是简单的利用一些JNI函数即实现了。so easy 。


一、在Native层返回一个字符串 Java层原型方法:

[java] view plain copy print ?
  1. public class HelloJni {
  2. ...
  3. public native void getAJNIString();
  4. ...
  5. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. * Class:com_feixun_jni_HelloJni
  3. * Method:getAJNIString
  4. * Signature: ()Ljava/lang/String;
  5. */
  6. //返回字符串
  7. JNIEXPORT jstring JNICALL Java_com_feixun_jni_HelloJni_getAJNIString(JNIEnv * env, jobject obj)
  8. {
  9. jstring str = env->newStringUTF("HelloJNI"); //直接使用该JNI构造一个jstring对象返回
  10. return str ;
  11. }


二、在Native层返回一个int型二维数组(inta[ ][ ]) Java层原型方法:


[java] view plain copy print ?
  1. public class HelloJni {
  2. ...
  3. //参数代表几行几列数组 ,形式如:int a[dimon][dimon]
  4. private native int[][] getTwoArray(int dimon) ;
  5. ...
  6. }

Native层该方法实现为 :
[java] view plain copy print ?
  1. /*
  2. * Class:com_feixun_jni_HelloJni
  3. * Method:getTwoArray
  4. * Signature: (I)[[I
  5. */
  6. //通过构造一个数组的数组, 返回 一个二维数组的形式
  7. JNIEXPORT jobjectArray JNICALL Java_com_feixun_jni_HelloJni_getTwoArray
  8. (JNIEnv * env, jobject object, jint dimion)
  9. {
  10. jclass intArrayClass = env->FindClass("[I"); //获得一维数组 的类引用,即jintArray类型
  11. //构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion
  12. jobjectArray obejctIntArray=env->NewObjectArray(dimion ,intArrayClass , NULL);
  13. //构建dimion个一维数组,并且将其引用赋值给obejctIntArray对象数组
  14. for( int i = 0 ; i< dimion; i++ )
  15. {
  16. //构建jint型一维数组
  17. jintArray intArray = env->NewIntArray(dimion);
  18. jint temp[10]; //初始化一个容器,假设 dimion< 10 ;
  19. for( int j = 0 ; j < dimion ; j++)
  20. {
  21. temp[j] = i + j; //赋值
  22. }
  23. //设置jit型一维数组的值
  24. env->SetIntArrayRegion(intArray, 0 , dimion ,temp);
  25. //给object对象数组赋值,即保持对jint一维数组的引用
  26. env->SetObjectArrayElement(obejctIntArray , i ,intArray);
  27. env->DeleteLocalRef(intArray); //删除局部引用
  28. }
  29. returnobejctIntArray; //返回该对象数组
  30. }


三、在Native层操作Java层的类 :读取/设置类属性
Java层原型方法:

[java] view plain copy print ?
  1. public class HelloJni {
  2. ...
  3. //在Native层读取/设置属性值
  4. public native void native_set_name() ;
  5. ...
  6. private String name = "I am at Java" ; //类属性
  7. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. * Class:com_feixun_jni_HelloJni
  3. * Method:native_set_name
  4. * Signature: ()V
  5. */
  6. //在Native层操作Java对象,读取/设置属性等
  7. JNIEXPORT void JNICALL Java_com_feixun_jni_HelloJni_native_1set_1name
  8. (JNIEnv *env , jobjectobj )//obj代表执行此JNI操作的类实例引用
  9. {
  10. //获得jfieldID 以及 该字段的初始值
  11. jfieldIDnameFieldId ;
  12. jclass cls = env->GetObjectClass(obj); //获得Java层该对象实例的类引用,即HelloJNI类引用
  13. nameFieldId = env->GetFieldID(cls , "name" , "Ljava/lang/String; "); //获得属性句柄
  14. if(nameFieldId == NULL)
  15. {
  16. cout << " 没有得到name 的句柄Id \n; " ;
  17. }
  18. jstring javaNameStr = (jstring)env->GetObjectField(obj ,nameFieldId); // 获得该属性的值
  19. const char * c_javaName = env->GetStringUTFChars(javaNameStr , NULL); //转换为 char *类型
  20. string str_name = c_javaName ;
  21. cout << "the name from java is " << str_name << endl ; //输出显示
  22. env->ReleaseStringUTFChars(javaNameStr , c_javaName); //释放局部引用
  23. //构造一个jString对象
  24. char * c_ptr_name = "I come from Native" ;
  25. jstring cName = env->NewStringUTF(c_ptr_name); //构造一个jstring对象
  26. env->SetObjectField(obj , nameFieldId , cName); // 设置该字段的值
  27. }



四、在Native层操作Java层的类:回调Java方法Java层原型方法:

[java] view plain copy print ?
  1. public class HelloJni {
  2. ...
  3. //Native层回调的方法实现
  4. public void callback(String fromNative){
  5. System.out.println(" I was invoked by native method############# " + fromNative);
  6. };
  7. public native void doCallBack(); //Native层会调用callback()方法
  8. ...
  9. // main函数
  10. public static void main(String[] args)
  11. {
  12. new HelloJni().ddoCallBack();
  13. }
  14. }

Native层该方法实现为 :


[java] view plain copy print ?
  1. /*
  2. * Class:com_feixun_jni_HelloJni
  3. * Method:doCallBack
  4. * Signature: ()V
  5. */
  6. //Native层回调Java类方法
  7. JNIEXPORT void JNICALL Java_com_feixun_jni_HelloJni_doCallBack
  8. (JNIEnv * env , jobject obj)
  9. {
  10. //回调Java中的方法
  11. jclass cls = env->GetObjectClass(obj); //获得Java类实例
  12. jmethodID callbackID = env->GetMethodID(cls , "callback" , "(Ljava/lang/String; )V") ; //或得该回调方法句柄
  13. if(callbackID == NULL)
  14. {
  15. cout << "getMethodId is failed \n" << endl ;
  16. }
  17. jstring native_desc = env->NewStringUTF(" I am Native");
  18. env->CallVoidMethod(obj , callbackID , native_desc); //回调该方法,并且传递参数值
  19. }


接下来,我们会操作复杂对象,也就是Java层的类,包括从Native层返回一个类以及传递一个类到Native层去, 这儿我们
使用的类非常简单,如下:
Student.java类
[java] view plain copy print ?
  1. package com.feixun.jni;
  2. public class Student
  3. {
  4. private int age ;
  5. private String name ;
  6. //构造函数,什么都不做
  7. public Student(){ }
  8. public Student(int age ,String name){
  9. this.age = age ;
  10. this.name = name ;
  11. }
  12. public int getAge() {
  13. return age;
  14. }
  15. public void setAge(int age) {
  16. this.age = age;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name){
  22. this.name = name;
  23. }
  24. public String toString(){
  25. return "name --- >" + name + "age --->" + age ;
  26. }
  27. }

五、在Native层返回一个复杂对象(即一个类咯)
Java层的方法对应为:

[java] view plain copy print ?
  1. public class HelloJni {
  2. ...
  3. //在Native层返回一个Student对象
  4. public native Student nativeGetStudentInfo() ;
  5. ...
  6. }

Native层该方法实现为 :
[java] view plain copy print ?
  1. /*
  2. * Class:com_feixun_jni_HelloJni
  3. * Method:nativeGetStudentInfo
  4. * Signature: ()Lcom/feixun/jni/Student;
  5. */
  6. //返回一个复杂对象
  7. JNIEXPORT jobject JNICALL Java_com_feixun_jni_HelloJni_nativeGetStudentInfo
  8. (JNIEnv * env, jobject obl)
  9. {
  10. //关于包描述符,这儿可以是 com/feixun/jni/Student 或者是 Lcom/feixun/jni/Student;
  11. //这两种类型 都可以获得class引用
  12. jclass stucls = env->FindClass("com/feixun/jni/Student"); //或得Student类引用
  13. //获得得该类型的构造函数函数名为 返回类型必须为 void 即 V
  14. jmethodID constrocMID = env->GetMethodID(stucls,"","(ILjava/lang/String; )V");
  15. jstring str = env->NewStringUTF(" come from Native ");
  16. jobject stu_ojb = env->NewObject(stucls,constrocMID,11,str); //构造一个对象,调用该类的构造函数,并且传递参数
  17. return stu_ojb ;
  18. }


六、从Java层传递复杂对象至Native层

Java层的方法对应为:
[java] view plain copy print ?
  1. public class HelloJni {
  2. ...
  3. //在Native层打印Student的信息
  4. public native voidprintStuInfoAtNative(Student stu);
  5. ...
  6. }

Native层该方法实现为 :


[java] view plain copy print ?
  1. /*
  2. * Class:com_feixun_jni_HelloJni
  3. * Method:printStuInfoAtNative
  4. * Signature: (Lcom/feixun/jni/Student; )V
  5. */
  6. //在Native层输出Student的信息
  7. JNIEXPORT void JNICALL Java_com_feixun_jni_HelloJni_printStuInfoAtNative
  8. (JNIEnv * env, jobject obj,jobject obj_stu) //第二个类实例引用代表Student类,即我们传递下来的对象
  9. {
  10. jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student类引用
  11. if(stu_cls == NULL)
  12. {
  13. cout << "GetObjectClass failed \n" ;
  14. }
  15. //下面这些函数操作,我们都见过的。O(∩_∩)O~
  16. jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //获得得Student类的属性id
  17. jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String; "); // 获得属性ID
  18. jint age = env->GetIntField(objstu , ageFieldID); //获得属性值
  19. jstring name = (jstring)env->GetObjectField(objstu , nameFieldID); //获得属性值
  20. const char * c_name = env->GetStringUTFChars(name ,NULL); //转换成 char *
  21. string str_name = c_name ;
  22. env->ReleaseStringUTFChars(name,c_name); //释放引用
  23. cout << " at Native age is :" << age << " # name is " << str_name << endl ;
  24. }


七、最后加个难度,即在Native层返回集合对象(留这儿,以后也好找点)


Java层的对应方法为:

[java] view plain copy print ?
  1. public class HelloJni {
  2. ...
  3. //在Native层返回ArrayList集合
  4. public native ArrayList native_getListStudents();
  5. ...
  6. }

Native层该方法实现为 :

[java] view plain copy print ?
  1. /*
  2. * Class:com_feixun_jni_HelloJni
  3. * Method:native_getListStudents
  4. * Signature: ()Ljava/util/ArrayList;
  5. */ //获得集合类型的数组
  6. JNIEXPORT jobject JNICALL Java_com_feixun_jni_HelloJni_native_getListStudents
  7. (JNIEnv * env, jobject obj)
  8. {
  9. jclass list_cls = env->FindClass("Ljava/util/ArrayList; "); //获得ArrayList类引用
  10. if(listcls == NULL)
  11. {
  12. cout << "listcls is null \n" ;
  13. }
  14. jmethodID list_costruct = env->GetMethodID(list_cls , "","()V"); //获得得构造函数Id
  15. jobject list_obj = env->NewObject(list_cls , list_costruct); //创建一个Arraylist集合对象
  16. //或得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ;
  17. jmethodID list_add= env->GetMethodID(list_cls,"add","(Ljava/lang/Object; )Z");
  18. jclass stu_cls = env->FindClass("Lcom/feixun/jni/Student; "); //获得Student类引用
  19. //获得该类型的构造函数函数名为 返回类型必须为 void 即 V
  20. jmethodID stu_costruct = env->GetMethodID(stu_cls , "", "(ILjava/lang/String; )V");
  21. for(int i = 0 ; i < 3 ; i++)
  22. {
  23. jstring str = env->NewStringUTF("Native");
  24. //通过调用该对象的构造函数来new 一个 Student实例
  25. jobject stu_obj = env->NewObject(stucls , stu_costruct , 10,str); //构造一个对象
  26. env->CallBooleanMethod(list_obj , list_add , stu_obj); //执行Arraylist类实例的add方法,添加一个stu对象
  27. }
  28. return list_obj ;
  29. }




【掌握JNI】最后,如何调用这些JNI函数,大家都懂的,直接调用即可,我就不在贴代码了,免得罗嗦。

    推荐阅读