jni应用(学习以及应用)
来源:互联网 发布:动画剧场版神作 知乎 编辑:程序博客网 时间:2024/06/11 06:31
要素 :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编程指南》
http://blog.csdn.net/qinjuning
一、类操作
jclass DefineClass (JNIEnv*env, jobject loader, const jbyte *buf , jsize bufLen);
功能:从原始类数据的缓冲区中加载类。
参数: env JNI接口指针。
loader 分派给所定义的类的类加载器。
buf 包含 .class文件数据的缓冲区。
bufLen 缓冲区长度。
返回值:返回 Java类对象。如果出错则返回NULL。
抛出: ClassFormatError 如果类数据指定的类无效。
ClassCircularityError 如果类或接口是自身的超类或超接口。
OutOfMemoryError 如果系统内存不足。
jclass FindClass (JNIEnv *env, const char*name);
功能:该函数用于加载本地定义的类。它将搜索由CLASSPATH环境变量为具有指定名称的类所指定的目录和 zip文件。
参数:env JNI接口指针。
name 类全名(即包名后跟类名,之间由"/"分隔).如果该名称以“[(数组签名字符)打头,则返回一个数组类。
返回值:返回类对象全名。如果找不到该类,则返回 NULL。
抛出: ClassFormatError如果类数据指定的类无效。
ClassCircularityError 如果类或接口是自身的超类或超接口。
NoClassDefFoundError 如果找不到所请求的类或接口的定义。
OutOfMemoryError 如果系统内存不足。
jclass GetObjectClass (JNIEnv *env, jobject obj);
功能:通过对象获取这个类。该函数比较简单,唯一注意的是对象不能为NULL,否则获取的class肯定返回也为NULL。
参数: env JNI接口指针。
obj Java类对象实例。
jclass GetSuperclass (JNIEnv*env, jclassclazz);
功能:获取父类或者说超类。 如果 clazz 代表类class而非类 object,则该函数返回由 clazz所指定的类的超类。如果 clazz
指定类 object或代表某个接口,则该函数返回NULL。
参数: env JNI接口指针。
clazz Java类对象。
返回值: 由 clazz 所代表的类的超类或 NULL。
jboolean IsAssignableFrom (JNIEnv *env, jclass clazz1, jclass clazz2);
功能:确定 clazz1的对象是否可安全地强制转换为clazz2。
参数: env JNI接口指针。
clazz1第一个类参数。
clazz2第二个类参数。
返回值: 下列某个情况为真时返回 JNI_TRUE:
1、 第一及第二个类参数引用同一个 Java类。
2、第一个类是第二个类的子类。
3、第二个类是第一个类的某个接口。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
二、异常操作
jint Throw(JNIEnv*env, jthrowable obj);
功能:抛出 java.lang.Throwable对象。
参数: env JNI接口指针。
obj java.lang.Throwable对象。
返回值: 成功时返回 0,失败时返回负数。
抛出: java.lang.Throwable对象 obj。
jint ThrowNew (JNIEnv *env, jclass clazz, const char *message);
功能:利用指定类的消息(由 message指定)构造异常对象并抛出该异常。
参数: env JNI接口指针。
clazz java.lang.Throwable的子类。
message 用于构造java.lang.Throwable对象的消息。
返回值: 成功时返回 0,失败时返回负数。
抛出: 新构造的 java.lang.Throwable对象。
jthrowable ExceptionOccurred (JNIEnv*env);
功能:确定是否某个异常正被抛出。在平台相关代码调用 ExceptionClear()或 Java 代码处理该异常前,异常将始终保持
抛出状态。
参数: env JNI接口指针。
返回值: 返回正被抛出的异常对象,如果当前无异常被抛出,则返回NULL。
void ExceptionDescribe (JNIEnv*env);
功能:将异常及堆栈的回溯输出到系统错误报告信道(例如 stderr)。该例程可便利调试操作。
参数:env JNI接口指针。
void ExceptionClear (JNIEnv *env);
功能:清除当前抛出的任何异常。如果当前无异常,则此例程不产生任何效果。
参数: env JNI接口指针。
void FatalError (JNIEnv*env, const char*msg);
功能:抛出致命错误并且不希望虚拟机进行修复。该函数无返回值。
参数: env JNI接口指针。
msg 错误消息。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
三、全局及局部引用
jobject NewGlobalRef (JNIEnv *env, jobjectobj);
功能:创建 obj参数所引用对象的新全局引用。obj参数既可以是全局引用,也可以是局部引用。全局引用通过调用
DeleteGlobalRef()来显式撤消。
参数:env JNI接口指针。
obj 全局或局部引用。
返回值: 返回全局引用。如果系统内存不足则返回 NULL。
void DeleteGlobalRef (JNIEnv *env, jobjectglobalRef);
功能:删除 globalRef所指向的全局引用。
参数: env JNI接口指针。
globalRef 全局引用。
void DeleteLocalRef (JNIEnv *env, jobjectlocalRef);
功能:删除 localRef所指向的局部引用。
参数: env JNI接口指针。
localRef 局部引用。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
四、对象操作
jobject AllocObject (JNIEnv *env, jclassclazz);
功能:分配新 Java对象而不调用该对象的任何构造函数。返回该对象的引用。clazz参数务必不要引用数组类。
参数: env JNI接口指针。
clazz Java类对象。
返回值: 返回 Java对象。如果无法构造该对象,则返回NULL。
抛出: InstantiationException:如果该类为一个接口或抽象类。
OutOfMemoryError:如果系统内存不足。
jobject NewObject (JNIEnv *env, jclass clazz, jmethodID methodID, ...); //参数附加在函数后面
jobject NewObjectA (JNIEnv *env, jclassclazz, jmethodID methodID, jvalue *args); //参数以指针形式附加
jobjectNewObjectV (JNIEnv *env , jclassclazz, jmethodIDmethodID, va_list args); //参数以"链表"形式附加
功能:构造新 Java对象。方法 ID指示应调用的构造函数方法。注意:该 ID特指该类class的构造函数ID,必须通过调用
GetMethodID()获得,且调用时的方法名必须为<init>,而返回类型必须为 void (V)。clazz参数务必不要引用数组类。
参数: env JNI接口指针。
clazz Java类对象。
methodID构造函数的方法 ID。
NewObject 的其它参数: 传给构造函数的参数,可以为空。
NewObjectA的其它参数: args:传给构造函数的参数数组。
NewObjectV的其它参数: args:传给构造函数的参数 va_list。
返回值: 返回 Java对象,如果无法构造该对象,则返回NULL。
抛出: InstantiationException 如果该类为接口或抽象类。
OutOfMemoryError 如果系统内存不足。
构造函数抛出的任何异常。
jclass GetObjectClass (JNIEnv *env, jobjectobj);
功能:返回对象的类。
参数: env JNI接口指针。
obj Java对象(不能为 NULL)。
返回值: 返回 Java类对象。
jboolean IsInstanceOf (JNIEnv*env, jobject obj, jclass clazz);
功能:测试对象是否为某个类的实例。
参数: env JNI接口指针。
obj Java对象。
clazzJava类对象。
返回值:如果可将 obj强制转换为 clazz,则返回 JNI_TRUE。否则返回 JNI_FALSE。NULL对象可强制转换为任何类。
jbooleanIsSameObject (JNIEnv*env, jobjectref1, jobject ref2);
功能:测试两个引用是否引用同一 Java对象。
参数: env JNI接口指针。
ref1 Java对象。
ref2 Java对象。
返回值: 如果 ref1和 ref2 引用同一 Java对象或均为 NULL,则返回 JNI_TRUE。否则返回 JNI_FALSE。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
五、 字符串操作
jstring NewString (JNIEnv *env, const jchar *unicodeChars, jsize len);
功能:利用 Unicode字符数组构造新的 java.lang.String对象。
参数: env:JNI接口指针。
unicodeChars:指向 Unicode字符串的指针。
len:Unicode字符串的长度。
返回值: Java字符串对象。如果无法构造该字符串,则为NULL。
抛出: OutOfMemoryError:如果系统内存不足。
jsize GetStringLength (JNIEnv*env, jstringstring);
功能:返回 Java字符串的长度(Unicode 字符数)。
参数: env:JNI接口指针。
string:Java字符串对象。
返回值: Java字符串的长度。
const jchar * GetStringChars (JNIEnv*env, jstring string, jboolean *isCopy);
功能:返回指向字符串的 Unicode字符数组的指针。该指针在调用 ReleaseStringchars()前一直有效。
如果 isCopy非空,则在复制完成后将 *isCopy设为 JNI_TRUE。如果没有复制,则设为JNI_FALSE。
参数: env:JNI接口指针。
string:Java字符串对象。
isCopy:指向布尔值的指针。
返回值: 指向 Unicode 字符串的指针,如果操作失败,则返回NULL。
void ReleaseStringChars (JNIEnv *env, jstring string, const jchar *chars);
功能:通知虚拟机平台相关代码无需再访问 chars。参数chars是一个指针,可通过 GetStringChars()从 string 获得。
参数: env:JNI接口指针。
string:Java字符串对象。
chars:指向 Unicode字符串的指针。
jstring NewStringUTF (JNIEnv*env, const char*bytes);
功能:利用 UTF-8字符数组构造新 java.lang.String对象。
参数: env:JNI接口指针。如果无法构造该字符串,则为 NULL。
bytes:指向 UTF-8字符串的指针。
返回值:Java字符串对象。如果无法构造该字符串,则为NULL。
抛出: OutOfMemoryError:如果系统内存不足。
jsize GetStringUTFLength (JNIEnv *env, jstringstring);
功能:以字节为单位返回字符串的 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。
void ReleaseStringUTFChars (JNIEnv *env, jstring string, const char *utf);
功能:通知虚拟机平台相关代码无需再访问 utf。utf参数是一个指针,可利用 GetStringUTFChars()获得。
参数: env:JNI接口指针。
string:Java字符串对象。
utf:指向 UTF-8字符串的指针。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
六、数组操作
jsize GetArrayLength (JNIEnv*env, jarrayarray);
功能:返回数组中的元素数。
参数: 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处的值 ,(可以强制转换类型).
jobject GetObjectArrayElement (JNIEnv *env, jobjectArray array, jsizeindex);
功能:返回 Object数组的元素。
参数: env:JNI接口指针。
array:Java数组。
index:数组下标。
返回值: Java对象。
抛出: ArrayIndexOutOfBoundsException:如果 index 不是数组中的有效下标。
void SetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index,jobject value);
功能:设置 Object数组的元素。
参数: env:JNI接口指针。
array:Java数组。
index:数组下标。
value:新值。
抛出: ArrayIndexOutOfBoundsException:如果index不是数组中的有效下标。
ArrayStoreException:如果 value的类不是数组元素类的子类。
New<PrimitiveType>Array方法类型
NativeType New<PrimitiveType>Array (JNIEnv *env, ArrayType array, jboolean*isCopy);
说明:用于构造新基本类型数组对象的一系列操作。下表说明了特定的基本类型数组构造函数。用户应把
New<PrimitiveType>Array替换为某个实际的基本类型数组构造函数例程名(见下表),然后将 ArrayType替换为
该例程相应的数组类型。
参数: env: JNI 接口指针。
length:数组长度。
返回值: Java数组。如果无法构造该数组,则为 NULL。
New<PrimitiveType>Array 方法组 数组类型
NewBooleanArray() jbooleanArray
NewByteArray() jbyteArray
NewCharArray() jcharArray
NewShortArray() jshortArray
NewIntArray() jintArray
NewLongArray() jlongArray
NewFloatArray() jfloatArray
NewDoubleArray() jdoubleArray
Get<PrimitiveType>ArrayElements 方法类型
NativeType *Get<PrimitiveType>ArrayElements (JNIEnv *env, ArrayType array, jboolean*isCopy);
说明:一组返回基本类型数组体的函数。结果在调用相应的 Release<PrimitiveType>ArrayElements()函数前将一直有效。
由于返回的数组可能是 Java数组的副本,因此对返回数组的更改不必在基本类型数组中反映出来,直到调用了
Release<PrimitiveType>ArrayElements()。 如果 isCopy不是 NULL,*isCopy在复制完成后即被设为 JNI_TRUE。如果
未复制,则设为JNI_FALSE。
使用说明:
将 Get<PrimitiveType>ArrayElements替换为表中某个实际的基本类型元素访问器例程名。
将 ArrayType替换为对应的数组类型。
将 NativeType替换为该例程对应的本地类型。
参数: env:JNI接口指针。
array:Java字符串对象。
isCopy:指向布尔值的指针。
返回值: 返回指向数组元素的指针,如果操作失败,则为 NULL。
不管布尔数组在 Java虚拟机中如何表示,GetBooleanArrayElements()将始终返回一个 jbooleans 类型的指针,其中每一
字节代表一个元素(开包表示)。内存中将确保所有其它类型。
Get<PrimitiveType>ArrayElements例程 数组类型 本地类型
GetBooleanArrayElements() jbooleanArray jboolean
GetByteArrayElements() jbyteArray jbyte
GetCharArrayElements() jcharArray jchar
GetShortArrayElements() jshortArray jshort
GetIntArrayElements() jintArray jint
GetLongArrayElements() jlongArray jlong
GetFloatArrayElements() jfloatArray jfloat
GetDoubleArrayElements() jdoubleArray jdouble
Release<PrimitiveType>ArrayElements 方法类型
void Release<PrimitiveType>ArrayElements (JNIEnv *env, ArrayType array, NativeType*elems,jintmode);
功能:通知虚拟机平台相关代码无需再访问 elems的一组函数。elems 参数是一个通过使用对应的
Get<PrimitiveType>ArrayElements()函数由 array 导出的指针。必要时,该函数将把对 elems的修改复制回基本
类型数组。mode参数将提供有关如何释放数组缓冲区的信息。如果elems不是 array 中数组元素的副本,mode将无效。
否则,mode将具有下表所述的功能:
模式 动作
0 复制回内容并释放elems缓冲区
JNI_COMMIT 复制回内容但不释放elems缓冲区
JNI_ABORT 释放缓冲区但不复制回变化
多数情况下,编程人员将把“0”传给 mode参数以确保固定的数组和复制的数组保持一致。其它选项可以使编程人员进一步
控制内存管理,但使用时务必慎重。
使用说明:
将 ArrayType替换为对应的数组类型。
将 NativeType替换为该例程对应的本地类型。
参数: env:JNI接口指针。
array:Java数组对象。
elems:指向数组元素的指针。
mode:释放模式。
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
Get<PrimitiveType>ArrayRegion 方法类型
void Get<PrimitiveType>ArrayRegion (JNIEnv *env, ArrayType array, jsize start,jsize len, NativeType*buf);
功能:将基本类型数组某一区域复制到缓冲区中的一组函数。
使用说明:
将 Get<PrimitiveType>ArrayRegion替换为下表的某个实际基本类型元素访问器例程名。
将 ArrayType替换为对应的数组类型。
将 NativeType替换为该例程对应的本地类型。
参数: env:JNI接口指针。
array:Java指针。
start:起始下标。
len:要复制的元素数。
buf:目的缓冲区。
抛出: ArrayIndexOutOfBoundsException:如果区域中的某个下标无效。
方法族如下:
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
Set<PrimitiveType>ArrayRegion 方法类型
void Set<PrimitiveType>ArrayRegion (JNIEnv *env, ArrayType array, jsizestart, jsize len, NativeType*buf);
功能:将基本类型数组的某一区域从缓冲区中复制回来的一组函数。
使用说明: 将 Set<PrimitiveType>ArrayRegion替换为表中的实际基本类型元素访问器例程名。
将 ArrayType替换为对应的数组类型。
将 NativeType替换为该例程对应的本地类型。
参数: env:JNI接口指针。
array: Java数组。
start:起始下标。
len:要复制的元素数。
buf:源缓冲区。
抛出: ArrayIndexOutOfBoundsException:如果区域中的某个下标无效。
Set<PrimitiveType>ArrayRegion 方法族 数组类型 本地类型
SetBooleanArrayRegion() jbooleanArray jboolean
SetByteArrayRegion() jbyteArray jbyte
SetCharArrayRegion() jcharArray jchar
SetShortArrayRegion() jshortArray jshort
SetIntArrayRegion() jintArray jint
SetLongArrayRegion() jlongArray jlong
SetFloatArrayRegion() jfloatArray jfloat
SetDoubleArrayRegion() jdoubleArray jdouble
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
六、访问对象的属性和方法
1、实例属性的访问
jfieldID GetFieldID (JNIEnv*env, jclass clazz, const char *name, const char*sig);
功能:返回类的实例(非静态)域的属性 ID。该域由其名称及签名指定。访问器函数的Get<type>Field及 Set<type>Field
系列使用域 ID检索对象域。GetFieldID()不能用于获取数组的长度域。应使用GetArrayLength()。
参数: env:JNI接口指针。
clazz:Java类对象。
name:该属性的Name名称
sig: 该属性的域签名。
返回值:属性ID。如果操作失败,则返回NULL。
抛出: NoSuchFieldError:如果找不到指定的域。
ExceptionInInitializerError:如果由于异常而导致类初始化程序失败。
OutOfMemoryError:如果系统内存不足。
Get<type>Field例程
NativeType Get<type>Field (JNIEnv*env, jobject obj, jfieldIDfieldID);
功能:该访问器例程系列返回对象的实例(非静态)域的值。要访问的域由通过调用GetFieldID()而得到的域 ID 指定。
参数: env:JNI接口指针。
obj:Java对象(不能为 NULL)。
fieldID:有效的域 ID。
返回值: 属性的内容。
Get<type>Field例程名 本地类型
GetObjectField() jobject
GetBooleanField() jboolean
GetByteField() jbyte
GetCharField() jchar
GetShortField() jshort
GetIntField() jint
GetLongField() jlong
GetFloatField() jfloat
GetDoubleField() jdouble
Set<type>Field 方法族
void Set<type>Field (JNIEnv *env, jobject obj, jfieldIDfieldID, NativeTypevalue);
功能:该访问器例程系列设置对象的实例(非静态)属性的值。要访问的属性由通过调用SetFieldID()而得到的属性 ID指定。
参数: env:JNI接口指针。
obj:Java对象(不能为 NULL)。
fieldID:有效的域 ID。
value:域的新值。
方法族 如下:
Set<type>Field 方法族 本地类型
SetObjectField() jobject
SetBooleanField() jboolean
SetByteField() jbyte
SetCharField() jchar
SetShortField() jshort
SetIntField() jint
SetLongField() jlong
SetFloatField() jfloat
SetDoubleField() jdouble
2、静态属性的访问 :也存在相同的方法,
jfieldID GetStaticFieldID (JNIEnv *env,jclass clazz, const char *name, const char*sig);
NativeType GetStatic<type>Field (JNIEnv*env,jclass classzz , jfieldIDfieldID);
void SetStatic<type>Field (JNIEnv *env,jclassclasszz, jfieldIDfieldID, NativeTypevalue);
它们与实例属性的唯一区别在于第二个参数jclass classzz代表的是类引用,而不是类实例。
3、调用实例方法
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char*name, const char *sig);
功能:返回类或接口实例(非静态)方法的方法 ID。方法可在某个 clazz 的超类中定义,也可从 clazz继承。该方法由其名称
和签名决定。 GetMethodID()可使未初始化的类初始化。要获得构造函数的方法 ID,应将 <init>作为方法名,同时将
void (V)作为返回类型。
参数: env:JNI接口指针。
clazz:Java类对象。
name:方法名。
sig:方法的签名。
返回值: 方法 ID,如果找不到指定的方法,则为 NULL。
抛出: NoSuchMethodError:如果找不到指定方法。
ExceptionInInitializerError:如果由于异常而导致类初始化程序失败。
OutOfMemoryError:如果系统内存不足。
Call<type>Method例程 、Call<type>MethodA例程 、Call<type>MethodV例程
NativeType Call<type>Method (JNIEnv*en v, jobject obj , jmethodIDmethodID, ...); //参数附加在函数后面,
NativeType Call<type>MethodA (JNIEnv *env, jobject obj, jmethodID methodID,jvalue *args); //参数以指针形式附加
NativeType Call<type>MethodV (JNIEnv *env, jobject obj,jmethodID methodID, va_listargs); //参数以"链表"形式附加
说明:这三个操作的方法用于从本地方法调用Java实例方法。它们的差别仅在于向其所调用的方法传递参数时所用的机制。
这三个操作将根据所指定的方法 ID调用 Java 对象的实例(非静态)方法。参数 methodID必须通过调用 GetMethodID()
来获得。当这些函数用于调用私有方法和构造函数时,方法 ID必须从obj 的真实类派生而来,而不应从其某个超类派生。
当然,附加参数可以为空。
参数: env:JNI接口指针。
obj:Java对象。
methodID:方法 ID。
返回值: 返回调用 Java方法的结果。
抛出: 执行 Java方法时抛出的异常。
下表根据结果类型说明了各个方法类型。用户应将Call<type>Method中的 type 替换为所调用方法的Java 类型(或使用表
中的实际方法名),同时将 NativeType替换为该方法相应的本地类型。省略掉了其他两种类型。
Java层返回值 方法族 本地返回类型NativeType
返回值为void: CallVoidMethod() A / V (无)
返回值为引用类型: CallObjectMethod( ) jobect
返回值为boolean: CallBooleanMethod ( ) jboolean
返回值为byte: CallByteMethod( ) jbyte
返回值char : CallCharMethod( ) jchar
返回值short CallShortMethod() jshort
返回值为int : CallIntMethod() jint
返回值为long: CallLongMethod() jlong
返回值为float: CallFloatMethod() jfloat
返回值为double: CallDoubleMethod() jdouble
4、调用静态方法:也存在如下方法群,
jfieldID GetStaticMethodID (JNIEnv *env,jclass clazz, const char *name, const char*sig);
NativeType Call<type>Method (JNIEnv*env,jclass classzz , jfieldIDfieldID);
它们与于实例方法的唯一区别在于第二个参数jclass classzz代表的是类引用,而不是类实例。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
七、注册本地方法
jint RegisterNatives (JNIEnv*env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
功能:向 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:如果找不到指定的方法或方法不是本地方法。
jint UnregisterNatives (JNIEnv*env, jclassclazz);
功能:取消注册类的本地方法。类将返回到链接或注册了本地方法函数前的状态。 该函数不应在常规平台相关代码中使用。
相反,它可以为某些程序提供一种重新加载和重新链接本地库的途径。
参数: env:JNI接口指针。
clazz:Java类对象。
返回值: 成功时返回“0”;失败时返回负数。
在Java存在两种数据类型:基本类型和引用类型,大家都懂的。
在JNI的世界里也存在类似的数据类型,与Java比较起来,其范围更具严格性,如下:
1、primitive types----基本数据类型,如:int、 float 、char等基本类型
2、referencetypes----引用类型,如:类、实例、数组。
特别需要注意:数组 ------不管是对象数组还是基本类型数组,都作为reference types存在。
1、primitive types (基本数据类型)映射参见下表:
这些基本数据类型都是可以在Native层直接使用的。
2、reference types (引用数据类型)映射参见下表
Java类型 NativeType 描述
注意:
1、引用数据类型则不能直接使用,需要根据JNI函数进行相应的转换后,才能使用
2、多维数组(包括二维数组)都是引用类型,需要使用 jobjectArray 类型存取其值;
例如:二维整型数组就是指向一位数组的数组,其声明使用方式如下:
[java] view plaincopyprint?
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
void set (byte[ ] bytes) (Ljava/util/Map;)
(Ljava/io/FileDescriptor;JJ)
(Landroid/os/Parcel)
(Ljava/lang/Object)
在编程时,如果是利用javah工具的话,这些都不需要我们手动编写对应的类型转换,如果不能用javah工具,就只能手动的
进行类型转换了。
《 Android的NDK开发(3)————JNI数据类型的详解》
在掌握了JNI函数的使用和相关类型的映射后,以及知晓何利用javah工具生成对应的jni函数以及如何生成动态
链接库 (windos下就是.dll库,Linux就是.so库了,不懂在Window下生成dll动态库的,具体流程可看我的这篇博客:
《Android中JNI的使用之一:Java原生JNI的使用、javah指令的使用以及图解教材》)。即可掌握JNI的使用了了。
总的来说,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 plaincopyprint?
1. public class HelloJni {
2. ...
3. public native void getAJNIString();
4. ...
5. }
Native层该方法实现为 :
[java] view plaincopyprint?
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 plaincopyprint?
1. public class HelloJni {
2. ...
3. //参数代表几行几列数组 ,形式如:int a[dimon][dimon]
4. private native int[][] getTwoArray(int dimon) ;
5. ...
6. }
Native层该方法实现为 :
[java] view plaincopyprint?
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.
11. jclass intArrayClass = env->FindClass("[I"); //获得一维数组 的类引用,即jintArray类型
12. //构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion
13. jobjectArray obejctIntArray = env->NewObjectArray(dimion ,intArrayClass , NULL);
14.
15. //构建dimion个一维数组,并且将其引用赋值给obejctIntArray对象数组
16. for( int i = 0 ; i< dimion ; i++ )
17. {
18. //构建jint型一维数组
19. jintArray intArray = env->NewIntArray(dimion);
20.
21. jint temp[10] ; //初始化一个容器,假设 dimion < 10 ;
22. for( int j = 0 ; j < dimion ; j++)
23. {
24. temp[j] = i + j ; //赋值
25. }
26.
27. //设置jit型一维数组的值
28. env->SetIntArrayRegion(intArray, 0 , dimion ,temp);
29. //给object对象数组赋值,即保持对jint一维数组的引用
30. env->SetObjectArrayElement(obejctIntArray , i ,intArray);
31.
32. env->DeleteLocalRef(intArray); //删除局部引用
33. }
34.
35. return obejctIntArray; //返回该对象数组
36. }
三、在Native层操作Java层的类:读取/设置类属性
Java层原型方法:
[java] view plaincopyprint?
1. public class HelloJni {
2. ...
3. //在Native层读取/设置属性值
4. public native void native_set_name() ;
5. ...
6.
7. private String name = "I am at Java" ; //类属性
8. }
Native层该方法实现为 :
[java] view plaincopyprint?
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 , jobject obj ) //obj代表执行此JNI操作的类实例引用
9. {
10. //获得jfieldID 以及 该字段的初始值
11. jfieldID nameFieldId ;
12.
13. jclass cls = env->GetObjectClass(obj); //获得Java层该对象实例的类引用,即HelloJNI类引用
14.
15. nameFieldId = env->GetFieldID(cls , "name" , "Ljava/lang/String;"); //获得属性句柄
16.
17. if(nameFieldId == NULL)
18. {
19. cout << " 没有得到name 的句柄Id \n;" ;
20. }
21. jstring javaNameStr = (jstring)env->GetObjectField(obj ,nameFieldId); // 获得该属性的值
22. const char * c_javaName = env->GetStringUTFChars(javaNameStr , NULL); //转换为 char *类型
23. string str_name = c_javaName ;
24. cout << "the name from java is " << str_name << endl ; //输出显示
25. env->ReleaseStringUTFChars(javaNameStr , c_javaName); //释放局部引用
26.
27. //构造一个jString对象
28. char * c_ptr_name = "I come from Native" ;
29.
30. jstring cName = env->NewStringUTF(c_ptr_name); //构造一个jstring对象
31.
32. env->SetObjectField(obj , nameFieldId , cName); // 设置该字段的值
33. }
四、在Native层操作Java层的类:回调Java方法
Java层原型方法:
[java] view plaincopyprint?
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.
10. // main函数
11. public static void main(String[] args)
12. {
13. new HelloJni().ddoCallBack();
14. }
15. }
Native层该方法实现为 :
[java] view plaincopyprint?
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.
12. jclass cls = env->GetObjectClass(obj);//获得Java类实例
13. jmethodID callbackID = env->GetMethodID(cls , "callback" , "(Ljava/lang/String;)V") ;//或得该回调方法句柄
14.
15. if(callbackID == NULL)
16. {
17. cout << "getMethodId is failed \n" << endl ;
18. }
19.
20. jstring native_desc = env->NewStringUTF(" I am Native");
21.
22. env->CallVoidMethod(obj , callbackID , native_desc); //回调该方法,并且传递参数值
23. }
接下来,我们会操作复杂对象,也就是Java层的类,包括从Native层返回一个类以及传递一个类到Native层去,这儿我们
使用的类非常简单,如下:
Student.java类
[java] view plaincopyprint?
1. package com.feixun.jni;
2.
3. public class Student
4. {
5. private int age ;
6. private String name ;
7. //构造函数,什么都不做
8. public Student(){ }
9.
10. public Student(int age ,String name){
11. this.age = age ;
12. this.name = name ;
13. }
14.
15. public int getAge() {
16. return age;
17. }
18. public void setAge(int age) {
19. this.age = age;
20. }
21. public String getName() {
22. return name;
23. }
24. public void setName(String name){
25. this.name = name;
26. }
27.
28. public String toString(){
29. return "name --- >" + name + " age --->" + age ;
30. }
31. }
五、在Native层返回一个复杂对象(即一个类咯)
Java层的方法对应为:
[java] view plaincopyprint?
1. public class HelloJni {
2. ...
3. //在Native层返回一个Student对象
4. public native Student nativeGetStudentInfo() ;
5. ...
6. }
Native层该方法实现为:
[java] view plaincopyprint?
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.
14. //获得得该类型的构造函数 函数名为 <init> 返回类型必须为 void 即 V
15. jmethodID constrocMID = env->GetMethodID(stucls,"<init>","(ILjava/lang/String;)V");
16.
17. jstring str = env->NewStringUTF(" come from Native ");
18.
19. jobject stu_ojb = env->NewObject(stucls,constrocMID,11,str); //构造一个对象,调用该类的构造函数,并且传递参数
20.
21.
22. return stu_ojb ;
23. }
六、从Java层传递复杂对象至Native层
Java层的方法对应为:
[java] view plaincopyprint?
1. public class HelloJni {
2. ...
3. //在Native层打印Student的信息
4. public native void printStuInfoAtNative(Student stu);
5. ...
6. }
Native层该方法实现为 :
[java] view plaincopyprint?
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.
11. jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student类引用
12.
13. if(stu_cls == NULL)
14. {
15. cout << "GetObjectClass failed \n" ;
16. }
17. //下面这些函数操作,我们都见过的。O(∩_∩)O~
18. jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //获得得Student类的属性id
19. jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String;"); // 获得属性ID
20.
21. jint age = env->GetIntField(objstu , ageFieldID); //获得属性值
22. jstring name = (jstring)env->GetObjectField(objstu , nameFieldID);//获得属性值
23.
24. const char * c_name = env->GetStringUTFChars(name ,NULL);//转换成 char *
25.
26. string str_name = c_name ;
27. env->ReleaseStringUTFChars(name,c_name); //释放引用
28.
29. cout << " at Native age is :" << age << " # name is " << str_name << endl ;
30. }
七、最后加个难度,即在Native层返回集合对象(留这儿,以后也好找点)
Java层的对应方法为:
[java] view plaincopyprint?
1. public class HelloJni {
2. ...
3. //在Native层返回ArrayList集合
4. public native ArrayList<Student> native_getListStudents();
5. ...
6. }
Native层该方法实现为:
[java] view plaincopyprint?
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.
11. if(listcls == NULL)
12. {
13. cout << "listcls is null \n" ;
14. }
15. jmethodID list_costruct = env->GetMethodID(list_cls , "<init>","()V"); //获得得构造函数Id
16.
17. jobject list_obj = env->NewObject(list_cls , list_costruct); //创建一个Arraylist集合对象
18. //或得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ;
19. jmethodID list_add = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z");
20.
21. jclass stu_cls = env->FindClass("Lcom/feixun/jni/Student;");//获得Student类引用
22. //获得该类型的构造函数 函数名为 <init> 返回类型必须为 void 即 V
23. jmethodID stu_costruct = env->GetMethodID(stu_cls , "<init>", "(ILjava/lang/String;)V");
24.
25. for(int i = 0 ; i < 3 ; i++)
26. {
27. jstring str = env->NewStringUTF("Native");
28. //通过调用该对象的构造函数来new 一个 Student实例
29. jobject stu_obj = env->NewObject(stucls , stu_costruct , 10,str); //构造一个对象
30.
31. env->CallBooleanMethod(list_obj , list_add , stu_obj); //执行Arraylist类实例的add方法,添加一个stu对象
32. }
33.
34. return list_obj ;
35. }
- jni应用(学习以及应用)
- Android 按键流程(InputManagerService 以及 JNI phoneWindowManger 应用)(and5.1)
- Android 按键流程(InputManagerService 以及 JNI phoneWindowManger 应用)(and5.1)
- 用Delphi开发JNI应用(转)
- 用Delphi开发JNI应用(转)
- 用Delphi开发JNI应用(转)
- Android jni 应用初探 (MAC 环境)
- jni应用(构造和方法)
- jni应用(*isCopy 返回的说明)
- JNI-VISA应用攻略
- Android JNI 应用实例
- JNI应用小结
- JNI 的应用
- java学习(6) java反射以及应用
- Bada学习- (三十二)C++以及Flash应用开发流程之创建应用档案
- Bada学习-(三十三) C++以及Flash应用开发流程之创建应用工程
- 加密算法学习以及base64加密解密应用
- 【oracle学习】9.触发器以及应用场景
- poj 2366
- Servlet未整理零碎笔记
- 关于String的几个有意思的小问题
- NOIP2011提高组day1第1题-铺地毯题解
- 交叉排序
- jni应用(学习以及应用)
- 原型模式--简单看法和实例
- (第20讲)关于排序的各种算法的汇总的题目
- 源码分析:ConcurrentHashMap实现分析
- Android 开发中的KotLin
- 不可错过的iOS开发技巧
- Android Studio 设置背景图片
- phpMyAdmin 出现 ---- 缺少 mysqli 扩展,请检查 PHP 配置。
- python如何获取百度搜索结果的真实URL