Android JNI函数

0.要素
1.类操作
2.异常操作
3.全局及局部引用
4.对象操作
5.字符串操作
6.数组操作
7.访问对象的属性和方法
7.1 实例属性的访问
7.2 静态属性的访问
7.3 调用实例方法
7.4 调用静态方法(也存在如下方法群)
8.注册本地方法

0.要素
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 编程指南》

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

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

(3)通过对象获取这个类
该函数比较简单,对象不能为 NULL,否则获取的 class 返回也为 NULL。
jclass GetObjectClass ( JNIEnv* env,
jobject obj);
参数:
env:JNI 接口指针。
obj:Java 类对象实例。
返回值:由 class 所代表的类的超类或 NULL。
示例:
jobject stu; //该变量由外部传入,实际上为 Student 类
jclass jstuClass = env->GetObjectClass(stu); //获取 Student 类对象

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

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

2.异常操作
(1) 抛出 java.lang.Throwable 对象
jint Throw( JNIEnv* env,
jthrowable obj);
参数:
env:JNI 接口指针。
obj:Java.lang.Throwable 对象。
返回值:成功时返回 0,失败时返回负数。
抛出:java.lang.Throwable 对象 obj。

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

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

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

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

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

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

(2)删除 globalRef 所指向的全局引用
void DeleteGlobalRef (JNIEnv* env,
jobject globalRef);
参数:
env:JNI 接口指针。
globalRef:全局引用。
返回值:无

(3)删除 localRef 所指向的局部引用
void DeleteLocalRef (JNIEnv* env,
jobject localRef);
参数:
env:JNI 接口指针。
localRef:局部引用。
返回值:无

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

(2)构造新 Java 对象
//参数附加在函数后面
jobject NewObject ( JNIEnv *env,
jclass clazz,
jmethodID methodID,
...);
//参数以指针形式附加
jobject NewObjectA (JNIEnv env,
jclass clazz,
jmethodID methodID,
jvalue
args);
//参数以"链表"形式附加
jobject NewObjectV (JNIEnv *env,
jclass clazz,
jmethodID methodID,
va_list args);

参数:
env:JNI 接口指针。
clazz:Java 类对象。
methodID:构造函数的方法 ID。
NewObject 的其它参数:传给构造函数的参数,可以为空 。
NewObjectA 的其它参数:args:传给构造函数的参数数组。
NewObjectV 的其它参数:args:传给构造函数的参数 va_list。
返回值:返回 Java 对象,如果无法构造该对象,则返回 NULL。
抛出异常:
InstantiationException:如果该类为接口或抽象类。
OutOfMemoryError:如果系统内存不足。
构造函数抛出的任何异常。

注意:该 ID 特指该类 clazz 的构造函数 ID,必须通过调用 GetMethodID()获得,且调
用时的方法名必须为 <init>,而返回类型必须为 void (V)。clazz 参数务必不要引
用数组类。

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

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

(5)判断两个引用是否引用同一 Java 对象
jboolean IsSameObject ( JNIEnv *env,
jobject ref1,
jobject ref2);
参数:
env:JNI 接口指针。
ref1:Java 对象。
ref2:Java 对象。
返回值:如果 ref1 和 ref2 引用同一 Java 对象或均为 NULL,则返回 JNI_TRUE。否则
返回 JNI_FALSE。

5.字符串操作
(1)利用 Unicode 字符数组构造新的 java.lang.String 对象
jstring NewString (JNIEnv env,
const jchar
unicodeChars,
jsize len);
参数:
env:JNI 接口指针。
unicodeChars:指向 Unicode 字符串的指针。
len:Unicode 字符串的长度。

返回值:Java 字符串对象。如果无法构造该字符串,则为 NULL。
抛出:OutOfMemoryError:如果系统内存不足。
示例:

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

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

(4)通知虚拟机平台相关代码释放 chars
void ReleaseStringChars(JNIEnv* env,
jstring string,
const jchar* chars);
参数:
env:JNI 接口指针。
string:Java 字符串对象。
chars:指向 Unicode 字符串的指针。
返回值:无

(5)利用 UTF-8 字符数组构造新 java.lang.String 对象
jstring NewStringUTF ( JNIEnv *env,
const char *bytes);
参数:
env:JNI 接口指针。
bytes:指向 UTF-8 字符串的指针。
返回值:Java 字符串对象。如果无法构造该字符串,则为 NULL。
抛出异常:
OutOfMemoryError:如果系统内存不足。
示例:
jstring jstring1 = env->NewStringUTF("native string");

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

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

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

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

(2)构造新的数组
它将保存类 elementClass 中的对象。所有元素初始值均设为 initialElement。
jarray NewObjectArray (JNIEnv *env,
jsize length,
class elementClass,
jobject initialElement);
参数:
env:JNI 接口指针。
length:数组大小。
elementClass:数组元素类。
initialElement:初始值(可为 NULL)
返回值:Java 数组对象。如果无法构造数组,则为 NULL。
抛出异常:
OutOfMemoryError:如果系统内存不足。
注意:使用该函数时,为了便于易操作性,我们一般可以用 jobjectArray 数组类型或
得返回值,例如:
jobjectArray objArray = env->NewObjectArray( );
//操作该对象,获得该 object 数组在索引 0 处的值 ,(可以强制转换类型).
env->GetObjectArrayElement(objArray, 0);

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

(4)设置 Object 数组的元素
void SetObjectArrayElement ( JNIEnv *env,
jobjectArray array,
jsize index,
jobject value);
参数:
env:JNI 接口指针。
array:Java 数组。
index:数组下标。
value:新的值。
返回值:无

抛出异常:
ArrayIndexOutOfBoundsException:如果 index 不是数组中的有效下标。
ArrayStoreException:如果 value 的类不是数组元素类的子类。
(5)New<PrimitiveType>Array 方法类型
用于构造新基本类型数组对象的一系列操作。下表说明了特定的基本类型数组构造函数。
用户应把 New<PrimitiveType>Array 替换为某个实际的基本类型数组构造函数名称,然后
将 ArrayType 替换为该名称相应的数组类型。

NativeType New<PrimitiveType>Array (JNIEnv* env,
ArrayType array,
jboolean* isCopy);
参数:
env:JNI 接口指针。
length:数组长度。
返回值:Java 数组。如果无法构造该数组,则为 NULL。

方法族如下:
New<PrimitiveType>Array 方法族 数组类型
NewBooleanArray() jbooleanArray
NewByteArray() jbyteArray
NewCharArray() jcharArray
NewShortArray() jshortArray
NewIntArray() jintArray
NewLongArray() jlongArray
NewFloatArray() jfloatArray
NewDoubleArray() jdoubleArray

(6)Get<PrimitiveType>ArrayElements 方法类型
一组返回基本类型数组体的函数。结果在调用相应的
Release<PrimitiveType>ArrayElements()函数前将一直有效。
由于返回的数组可能是 Java 数组的副本,因此对返回数组的更改不必在基本类型
数组中反映出来,直到调用了 Release<PrimitiveType>ArrayElements()。如果 isCopy
不是 NULL,*isCopy 在复制完成后即被设为 JNI_TRUE。如果未复制,则设为 JNI_FALSE。
使用说明:
1.将 Get<PrimitiveType>ArrayElements 替换为表中某个实际的基本类型元素访问
器例程名。
2.将 ArrayType 替换为对应的数组类型。
3.将 NativeType 替换为该例程对应的本地类型。

NativeType* Get<PrimitiveType>ArrayElements(JNIEnv* env,
ArrayType array,
jboolean* isCopy);
参数:
env:JNI 接口指针。

array:Java 字符串对象。
isCopy:指向布尔值的指针。
返回值:返回指向数组元素的指针,如果操作失败,则为 NULL。
不管布尔数组在 Java 虚拟机中如何表示,GetBooleanArrayElements()将始终返回一
个 jbooleans 类型的指针,其中每一字节代表一个元素(开包表示)。内存中将确保所有
其它类型。

方法族如下:

Get<PrimitiveType>ArrayElements 方法族 数组类型 本地类型
etBooleanArrayElements() jbooleanArray jboolean
GetByteArrayElements() jbyteArray jbyte
GetCharArrayElements() jcharArray jchar
GetShortArrayElements() jshortArray jshort
GetIntArrayElements() jintArray jint
GetLongArrayElements() jlongArray jlong
GetFloatArrayElements() jfloatArray jfloat
GetDoubleArrayElements() jdoubleArray jdouble

(7)Release<PrimitiveType>ArrayElements 方法类型
通知虚拟机平台相关代码无需再访问 elems 的一组函数。elems 参数是一个通过使用
对应的 Get<PrimitiveType>ArrayElements()函数由 array 导出的指针。必要时,该函数
将把对 elems 的修改复制回基本类型数组。mode 参数将提供有关如何释放数组缓冲区的
信息。如果 elems 不是 array 中数组元素的副本,mode 将无效。否则,mode 将具有下表
所述的功能:

模式 动作
0 复制回内容并释放 elems 缓冲区
JNI_COMMIT 复制回内容但不释放 elems 缓冲区
JNI_ABORT 释放缓冲区但不复制回变化

多数情况下,编程人员将把“0”传给 mode 参数以确保固定的数组和复制的数组保
持一致。其它选项可以使编程人员进一步控制内存管理,但使用时务必慎重。
使用说明:
1.将 ArrayType 替换为对应的数组类型。
2.将 NativeType 替换为该例程对应的本地类型。

方法族如下:

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

(8)Get<PrimitiveType>ArrayRegion 方法类型
将基本类型数组某一区域复制到缓冲区中的一组函数。
使用说明:
1.将 Get<PrimitiveType>ArrayRegion 替换为下表的某个实际基本类型元素访问器例
程名。
2.将 ArrayType 替换为对应的数组类型。
3.将 NativeType 替换为该例程对应的本地类型。

void Get<PrimitiveType>ArrayRegion ( JNIEnv *env,
ArrayType array,
jsize start, jsize len,
NativeType *buf);
参数;
env:
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

(8)Set<PrimitiveType>ArrayRegion 方法类型
将基本类型数组的某一区域从缓冲区中复制回来的一组函数。
使用说明:
1.将 Set<PrimitiveType>ArrayRegion 替换为表中的实际基本类型元素访问器例程名。
2.将 ArrayType 替换为对应的数组类型。
3.将 NativeType 替换为该例程对应的本地类型。
void Set<PrimitiveType>ArrayRegion ( JNIEnv *env,
ArrayType array,
jsize start,jsize len,
NativeType *buf);
参数;

env:JNI 接口指针。
array:ava 数组。
start:起始下标。
len:要复制的元素数个数
buf:源缓冲区。
返回值:无
抛出异常:
ArrayIndexOutOfBoundsException:如果区域中的某个下标无效。

方法族如下:
Set<PrimitiveType>ArrayRegion 方法族 数组类型 本地类型
SetBooleanArrayRegion() booleanArray jboolean
SetByteArrayRegion() jbyteArray jbyte
SetCharArrayRegion() jcharArray jchar
SetShortArrayRegion() jshortArray jshortF
SetIntArrayRegion() jintArray jint
SetLongArrayRegion() jlongArray jlong
SetFloatArrayRegion() jfloatArray jfloat
SetDoubleArrayRegion() jdoubleArray jdouble

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

Java 层:所在类 com.hello.hellojni3.Student
String name;
Native 层:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_hello_hellojni3_MainActivity_setNameFromNative(JNIEnv* env,
jobject instance,
jobject stu) { //Student 对象
// TODO
// 1. 获取类型
jclass jstuClass = env->FindClass("com/hello/hellojni3/Student");
// 2. 获取域 ID
jfieldID jfieldID1 =
env->GetFieldID(jstuClass, "name", "Ljava/lang/StrinGetFieldID
jstring jstring1 = env->NewStringUTF("native string");
env->SetObjectField(stu, jfieldID1, jstring1);
return jstring1;
}

(2)Get<type>Field 方法类型
该访问器例程系列返回对象的实例(非静态)域的值。要访问的域由通过调用 GetFieldID()
而得到的域 ID 指定。
NativeType Get<type>Field (JNIEnv* env,
jobject obj,
jfieldID fieldID);
参数:
env:JNI 接口指针。
obj:ava 对象(不能为 NULL)
fieldID:有效的域 ID
返回值:属性的内容。

方法族如下:
Get<type>Field 本地类型
GetObjectField() jobject
GetBooleanField() jboolean
GetByteField() jbyte
GetCharField() jchar
GetShortField() jshort
GetIntField() jint
GetLongField() jlong
GetFloatField() jfloat
GetDoubleField() jdouble

(3)Set<type>Field 方法类型
该访问器例程系列设置对象的实例(非静态)属性的值。要访问的属性由通过调用
SetFieldID() 而得到的属性 ID 指定。
void Set<type>Field (JNIEnv *env,
jobject obj,
jfieldID fieldID,
NativeType value);
参数:
env:JNI 接口指针。
obj:Java 对象(不能为 NULL)。
fieldID:有效的域 ID。
value:新的值。
返回值:无

方法族如下:
Set<type>Field 方法族 本地类型
SetObjectField() jobject
SetBooleanField() jboolean
SetByteField() jbyte
SetCharField() jchar
SetShortField() jshort
SetIntField() jint
SetFloatField() jfloat
SetDoubleField() jdouble

7.2 静态属性的访问
jfieldID GetStaticFieldID ( JNIEnv *env,
jclass clazz,
const char *name,
const char sig);
NativeType GetStatic<type>Field ( JNIEnv
env,
jclass clazz,
jfieldID fieldID);
void SetStatic<type>Field ( JNIEnv *env,
jclass clazz,
jfieldID fieldID,
NativeType value);
参数:
env:JNI 接口指针。
clazz:Java 类对象
name:方法名
sig:方法的签名
fieldID:方法 ID,如果找不到指定的方法,则为 NULL。
value:新的值
它们与实例属性的唯一区别在于第二个参数 jclass clazz 代表的是类引用,而不是类
实例。

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

示例:
java 层:方法所在类为 com/hello/hellojni3/Student
public int getId() {return id;}
Native 层:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_hello_hellojni3_MainActivity_CallgetNameFromNative(
JNIEnv *env, jobject instance, jobject stu) {
// 1. 获取类型(参见 FindClass 方法)
jclass jstuClass =
env->FindClass("com/hello/hellojni3/Student");
// jclass clazz:类型
// const char *name:名称
// const char *sig:签名
// 2. 获取方法 ID
jmethodID jmethodID1 = env->GetMethodID(jstuClass, "getId", "()I");
// 3. 调用方法
jint jint1 = env->CallIntMethod(stu, jmethodID1);
return jstring1;

(2)Call<type>Method,Call<type>MethodA,Call<type>MethodV 方法类型
//参数附加在函数后面
NativeType Call<type>Method (JNIEnv* env,
jobject obj,
jmethodID methodID,
...);
//参数以指针形式附加
NativeType Call<type>MethodA (JNIEnv *env,
jobject obj,
jmethodID methodID,
jvalue *args);
//参数以"链表"形式附加
NativeType Call<type>MethodV (JNIEnv *env,
jobject obj,
jmethodID methodID,
va_list args);
参数:
env:
obj:Java 对象
methodID:方法 ID
args:传给构造函数的参数数组。
返回值:返回调用 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

7.4 调用静态方法(也存在如下方法群)
jfieldID GetStaticMethodID (JNIEnv *env,
jclass class,
20 / 21
20 / 21
const char *name,
const char sig);
NativeType Call<type>Method (JNIEnv
env,
jclass class,
jfieldID fieldID);
参数:
env:JNI 接口指针。
class:Java 类对象。
name:该属性的 Name 名称。
sig:该属性的域签名。
fieldID:有效的域 ID
它们与于实例方法的唯一区别在于第二个参数 jclass class 代表的是类引用,而不是
类实例。

8.注册本地方法
(1)向 class 参数指定的类注册本地方法
methods 参数将指定 JNINativeMethod 结构的数组,其中包含本地方法的名称、签名和
函数指针。nMethods 参数将指定数组中的本地方法数。
jint RegisterNatives (JNIEnv *env,
jclass class,
const JNINativeMethod *methods,
jint nMethods);
参数:
env:JNI 接口指针。
class:Java 类对象。
methods:类中本地方法和具体实现方法的映射指针。
nMethods:类中的本地方法数。
返回值:成功时返回 "0";失败时返回负数。
抛出异常:
NoSuchMethodError:如果找不到指定的方法或方法不是本地方法。

JNINativeMethod 结构定义如下所示:
typedef struct {
char *name; //方法名
char *signature; //方法签名
void *fnPtr; //方法指针(Native 中所对应的方法)
} JNINativeMethod;

示例:
JNINativeMethod jniNativeMethod = {
"stringFromJNI1", //方法名
"(I)Ljava/lang/String;", //方法签名
(void*)stringFromJNI1 //指向方法所在的地址
};

extern "C"
jstring JNICALL stringFromJNI1(
JNIEnv env, jobject instance, jint n) {
// TODO
if(n < 2){
return env->NewStringUTF("C++");
} else {
return env->NewStringUTF("C#");
}
}
extern "C"
JNIEXPORT jint JNICALL JNI_OnLoad(
JavaVM
vm, // java 虚拟机指针
void* reserved // 保留
){
// 获取当前环境指针
JNIEnv* env = NULL;
jint ret = vm->GetEnv((void*)&env, JNI_VERSION_1_6);
if(ret != JNI_OK){
return 0;
}
// 动态的注册 jni 函数
// 需要先获取环境指针
// 获取类类型
const char
className = "com/bluelesson/hellojni3/MainActivity";
jclass clazz = env->FindClass(className);
env->RegisterNatives(clazz,&jniNativeMethod,1);
// 返回 版本
return JNI_VERSION_1_6;
}

函数指针通常必须有下列签名:
ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...);

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容