JNI函数(一)

Posted by Haiden on April 5, 2019

JNI开发要注意标记为must的限制。例如,当看到一个JNI函数表示 must 接受一个不为null的对象,有义务一定不要传null给该JNI函数,因为JNI函数在内部并不会检查空指针,需要在调用之前自己检查。

一、接口函数表

所有的JNI函数都是通过传递进来固定的第一个参数 JNIEnv 参数来进行访问到的。JNIEnv 类型是一个指向所有JNI函数指针集合的指针。

1
typedef const struct JNINativeInteface *JNIEnv;

二、版本信息

返回JNI的版本号。

1
2
3
4
5
6
7
8
9
10
11
12
jint GetVersion(JNIEnv *env);
--------------------------------
//参数:
- env: jni接口指针

//返回值:
返回一个值,其中高位为major版本号返回,低位为minor版本号。

 JDK/JRE 1.1中返回 0x00010001
 JDK/JRE 1.2中返回 0x00010002
 JDK/JRE 1.4中返回 0x00010004
 JDK/JRE 1.6中返回 0x00010006 

三、操作类

3.1 DefineClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
jclass DefineClass(JNIEnv *env, const char *name, jobject loader,
                  const jbyte *buf, jsize bufLen);
----------------------------
//参数:
envJNI接口指针
name:需要加载的类或接口的短名字,这个字符串使用MUTF-8编码。
loader:指派用来加载类的ClassLoader
buf:包含 .class 文件数据的 buffer
bufLen: buffer的长度

//返回值:
返回加载的Java类对象(java class object),或者如果出现错误则返回NULL

//抛出异常:
ClassFormatError :如果传入的class内容不是一个有效的class文件。
ClassCircularityError:如果classinterface是它自己的父类或父接口,造成循环层级关系。
OutOfMemoryError:如果系统在载入的过程中内存不足。
SecurityException :如果调用者启动载入java包内置的类,引发安全隐患。

3.2 FindClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
jclass FindClass(JNIEnv *env, const char *name);
--------------------------------
//第二个 name 参数,使用全称类名或数组类型签名(array type signature)
//例如:String类的全称类名为 "java/lang/String"
  
//参数:
envJNI接口指针

//name:全称的类名(包名以 / 作为分隔符, 然后紧跟着类名),如果名字以 [开头(数组签名标识符),则返回一个数组的类,这个字符串也是MUTF-8。

//返回值:
指定名称的类的对象(a class object),或者在没有找到对应类时返回 NULL
  
//抛出异常:
ClassFormatError :如果class内容不是一个有效的class文件。
ClassCircularityError:如果classinterface是它自己的父类或父接口,造成循环层级关系。
OutOfMemoryError:如果系统在载入的过程中内存不足。
NoClassDefFoundError:如果指定的类或接口没有被找到。(当namenull或超长时也会抛出这个异常)

3.3 GetSuperclass

只要传入的 clazz 参数不是 java/lang/Object 则返回该类的父类。

如果传入的 clazz 参数是 java/lang/Object 则返回NULL,因为它没有父类。当传入的是一个接口,而不是类时,也返回 NULL

1
2
3
4
5
6
7
8
9
jclass GetSuperclass(JNIEnv *env, jclass clazz);
---------------------------
//参数:
envJNI接口指针
clazz Java类对象(java class object
  
//返回值:
返回传入的 clazz 的父类,或 NULL .  

3.4 IsAssignableForm

检查 clazz1 的对象是否能被安全的转型(cast)为 clazz2

1
2
3
4
5
6
7
8
9
10
11
12
jboolean IsAssignableFrom(JNIEnv *env, jclass class1, jclass clazz2);
---------------------------------

//env:JNI接口指针
clazz1:第一个class参数(需要转型的类)
clazz2:第二个class参数(转型的目标类)

//返回值:
如果是以下情况则返回 JNI_TRUE :
  clazz1  clazz2 指向同一个java
  clazz1  clazz2 的子类。(向上转型是安全的)
  clazz1  clazz2(接口)的实现类。(也属于向上转型)

四、异常

4.1 Throw

触发一个 java.lang.Throwable 对象的异常被抛出。

1
2
3
4
5
6
7
8
9
10
11
jint Throw(JNIEnv *env, jthrowable obj);
--------------------------
//参数:
envJNI接口指针
obj java.lang.Throwable 对象

//返回值:
成功则返回0 失败时返回赋值

//抛出异常:
抛出 java.lang.Throwable 对象

4.2 ThrowNew

Exception对象的构造器函数,message为异常的错误消息,clazz为异常的类。

1
2
3
4
5
6
7
8
9
10
11
jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
---------------------------
//参数:
envJNI接口指针
clazzjava.lang.Throwable 的子类
message 用于创建 java.lang.Throwable 对象时传入的错误消息。这个是字符串是MUTF-8编码。

//返回值:
成功则返回0 失败时返回赋值
抛出异常:
抛出刚构造出来的 java.lang.Throwable 对象

4.3 ExceptionOccurred

检查是否有异常被抛出。这个异常在本地方法调用 ExceptionClear() 方法或被Java代码处理这个异常之前都会保持在被抛出状态。

1
2
3
4
5
6
7
jthrowable ExceptionOccurred(JNIEnv *env);
------------------------
//参数:
envJNI接口指针

//返回值:
返回过程中抛出的异常,或没有异常被抛出时返回 NULL 

4.4 ExceptionDescribe

打印一个异常的stack trace到系统的错误输出,例如 stderr 这是为了调试提供便利。

1
2
3
4
void ExceptionDescribe(JNIEnv *env);
-------------------------
//参数:
envJNI接口指针  

4.5 ExceptionClear

清理任何即将抛出的异常。如果没有异常被抛出,则不起任何作用。

1
2
3
4
void ExceptionClear(JNIEnv *env);
-----------------------------
//参数:
envJNI接口指针

4.6 FatalError

抛出一个严重错误,并不希望虚拟机恢复。

1
2
3
4
5
void FatalError(JNIEnv *env, const char *msg);
---------------------------
//参数:
envJNI接口指针
msg  错误消息,这个字符串为MUTF-8编码  

4.7 ExceptionCheck

一个快速函数用于检查是否有被抛出的异常,而不创建一个这个异常的局部引用。

1
2
3
4
5
6
7
jboolean ExceptionCheck(JNIEnv *env);
--------------------------
//参数:
envJNI接口指针

//返回值:
有一个即将被抛出的异常时返回 JNI_TURE ,没有则返回 JNI_FALSE  

五、全局和局部引用

局部引用只在本地方法执行的过程有有效。在本地方法返回时它们会被自动释放掉。所有的局部引用都有消耗一些Java虚拟机的资源。

5.1 NewGlobalRef

创建一个 obj 参数对象的全局引用

1
2
3
4
5
6
7
8
jobject NewGlobalRef(JNIEnv *env, jobject obj);
--------------------------
//参数:
env JNI接口指针
obj:一个全局或本地的对象引用

//返回值:
返回一个全局引用,或者当系统内存不足时返回 NULL

5.2 DeleteGlobalRef

删除指向 gloablRef 的全局变量。

1
2
3
4
5
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
-------------------------
//参数:
env JNI接口指针
globalRef: 需要删除的全局引用

5.3 DeleteLocalRef

删除 localRef 的局部引用。

1
2
3
4
5
void DeleteLocalRef(JNIEnv *env, jobject localRef);
--------------------------
//参数:
env JNI接口指针
localRef:需要删除的局部引用。

5.4 EnsureLocalCapacity

确保在当前线程中至少还有 capacity 个本地引用可能被创建。在每进入一个本地方法前,Java虚拟机自动确保至少可以上传 16 个局部引用。

1
2
3
4
5
6
7
8
jint EnsureLocalCapacity(JNIEnv *env, jint capacity);
--------------------------
//参数:
env JNI接口指针
capacity:

//返回值:
返回 0 表示还可以创建;否则返回一个负数,并抛出 OutOfMemoryError   

5.5 PushLocalFrame

创建一个局部引用的栈帧,其可以创建最多 capacity 个局部引用。在之前栈帧中已经创建的局部引用在当前栈帧中依然有效。

1
2
3
4
5
6
7
8
jint PushLocalFrame(JNIEnv *env, jint capacity);
-------------------------
 //参数:
env JNI接口指针
capacity:最多可创建的局部引用数

//返回值:
成功创建则返回0,失败则返回一个负数,并抛出 OutOfMenoryError  

5.6 PopLocalFrame

将当前栈帧出栈,释放所有局部引用

1
2
3
4
5
6
7
8
jobject PopLocalFrame(JNIEnv *env, jobject result);
-----------------------
//参数:
env JNI接口指针
result: 传入 NULL 表示不需要返回上一栈帧的引用。

//返回值:
返回给定 result 对象的上一个本地引用栈帧本地引用

5.7 NewLocalRef

创建一个局部引用同样指向 ref

1
2
3
4
5
6
7
8
jobject NewLocalRef(JNIEnv *env, jobject ref);
--------------------------
//参数:
env JNI接口指针
ref:可能是一个全局引用,或一个局部引用.
  
//返回值:
返回创建的局部引用,或者如果ref传入null,则返回 NULL  

六、弱全局引用

弱全局引用是一种特殊的全局引用。和普通全局引用不同的是,一个弱全局引用允许其指向的Java对象可以被垃圾回收。

弱全局引用可以在任何可以使用局部引用或全局引用的地方一样的使用。当垃圾回收器开始回收时,它会将只指向软引用的对象回收掉。一个弱全局引用指向的被释放的对象,其功能上相当于 NULL

弱全局引用是Java弱引用(Java Weak References)的一个简单实现版本

6.1 NewWeakGlobalRef

创建一个新的弱全局引用。

1
2
3
4
5
6
7
8
jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);
-------------------------
//参数:
env JNI接口指针
obj:一个全局引用或局部引用对象。

//返回值:
返回刚创建的新弱全局引用,如果obj参数为null或虚拟机内存不足时则返回NULL,如果虚拟机内存不足,还会抛出 OutOfMemoryError

6.2 DeleteWeakGlobalRef

删除一个弱引用。

1
2
3
4
5
oid DeleteWeakGlobalRef(JNIEnv *env, jweak obj);
--------------------------
//参数:
env JNI接口指针
jweak :需要删除的弱引用  

七、操作对象

7.1 AllocObject

在不调用类的任何一个构造器来分配一个新的Java对象。

1
2
3
4
5
6
7
8
9
10
11
12
jobject AllocObject(JNIEnv *env, jclass clazz);
--------------------------
//参数:
env JNI接口指针
clazz:需要初始化的类,不能是一个数组类型的类。 

//返回值:
返回新的尚未初始化的Java对象的引用。如果不能分配对象则返回 NULL

//抛出异常:
InstantiationException 如果传入的 clazz 是一个抽象类或接口。
OutOfMemoryError 如果系统没有内存时。

7.2 NewObject

构造(初始化)一个新的Java对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
---------------------------
//参数:
env JNI接口指针
clazz : 需要初始化对象的类
methodID:构造器方法ID  
参数methodID指向需要被调用的构造器函数,这个methodID必须使用 GetMethodID() 来获取  

//返回值:
返回一个Java对象,无法被构造时返回 NULL

//抛出异常:
InstantiationException 如果传入的 clazz 是一个抽象类或接口。
OutOfMemoryError 如果系统没有内存时。
其他所有构造器可能抛出的异常

7.3 NewObjectA

构造(初始化)一个新的Java对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
----------------------------
//参数:
env JNI接口指针
clazz : 需要初始化对象的类
methodID:构造器方法ID
const jvalue *args: 构造器参数

//返回值:
返回一个Java对象,无法被构造时返回 NULL

//抛出异常:
InstantiationException 如果传入的 clazz 是一个抽象类或接口。
OutOfMemoryError 如果系统没有内存时。
其他所有构造器可能抛出的异常

7.4 NewObjectV

构造(初始化)一个新的Java对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
--------------------------
//参数:
env JNI接口指针
clazz : 需要初始化对象的类
methodID:构造器方法ID
va_list args : 构造器函数

//返回值:
返回一个Java对象,无法被构造时返回 NULL

//抛出异常:
InstantiationException 如果传入的 clazz 是一个抽象类或接口。
OutOfMemoryError 如果系统没有内存时。
其他所有构造器可能抛出的异常

7.5 GetObjectClass

获取一个对象的class。

1
2
3
4
5
6
7
8
jclass GetObjectClass(JNIEnv *env, jobject obj);
-------------------------
//参数:
env JNI接口指针
obj:需要获取class的对象( 必须 不能为 NULL )
  
//返回值:
一个Java类对象(Java class object

7.6 GetObjectRefType

返回对象的引用类型,参数obj可能是局部变量,全局变量,或弱全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);
------------------------
//参数:
env JNI接口指针
obj 一个局部变量或全局或弱全局引用。

//返回值:
如果一下的几种值(定义为 jobjectRefType 枚举值)
无效引用: JNIInvalidRefType = 0 
局部引用: JNILocalRefType = 1
全局引用: JNIGlobalRefType = 2
弱全局引用:JNIWeakGlobalRefType = 3
  
//无效引用是没有指向有效的值。指向的地址不是在内存中分配的地址。

7.7 IsInstanceOf

检查一个对象是不是一个类的实例。

1
2
3
4
5
6
7
8
9
10
jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
-----------------------
//参数:
env JNI接口指针
objJava对象
clazzJava类对象(java class object

//返回值:
如果obj可以被castclazz,则返回 JNI_TURE ,否则返回 JNI_FALSE 
NULL 对象不能cast成任何clazz

7.8 IsSameObject

检查两个引用是否指向同一个Java对象。

1
2
3
4
5
6
7
8
9
10
jboolean IsSameObject(JNIEnv *env, jobject ref1, jobject ref2);
-----------------------
//参数:
env JNI接口指针
ref1: java对象1
ref2: java对象2

//返回值:
如果指向的是同一个Java对象或两个都是 NULL,则返回 JNI_TRUE 
否则返回 JNI_FALSE

八、访问对象成员域

8.1 GetFieldID

返回一个class的指定成员域的fieldID,成员域根据它的名字和签名来指定。返回的fieldID可以用来调用 Get<type>FieldSet<type>Field 来访问具体的成员域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
------------------------
//参数:
env JNI接口指针
clazz java class object
name :成员域的名称(MUTF-8编码)
sig  成员域的签名(MUTF-8编码)

//返回值:
返回成员域的fieldID,操作失败则返回 NULL

//抛出异常:
NoSuchFieldError 如果没有指定的字段存在
ExceptionInInitializerError 如果在初始化类的过程中出现异常
OutOfMemoryError 如果系统内存不足时

8.2 GetField

用于获取对象的非静态成员域。fieldID可以通过 GetFieldId() 函数来获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jobject GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID);
jboolean GetBooleanField(JNIEnv *env, jobject obj, jfieldID fieldID);
jbyte GetByteField(JNIEnv *env, jobject obj, jfieldID fieldID);
jchar GetCharField(JNIEnv *env, jobject obj, jfieldID fieldID);
jshort GetShortField(JNIEnv *env, jobject obj, jfieldID fieldID);
jint GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID);
jlong GetLongField(JNIEnv *env, jobject obj, jfieldID fieldID);
jfloat GetFloatField(JNIEnv *env, jobject obj, jfieldID fieldID);
jdouble GetDoubleField(JNIEnv *env, jobject obj, jfieldID fieldID);
-----------------------------
//参数:
env JNI接口指针
objectjava对象(必须不能为 NULL
fieldId :一个有效的fieldID

//返回值:
返回成员域的内容

8.3 SetField

一组设置基本类型成员域的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void SetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID, jobject value);
void SetBooleanField(JNIEnv *env, jobject obj, jfieldID fieldID, jboolean value);
void SetByteField(JNIEnv *env, jobject obj, jfieldID fieldID, jbyte value);
void SetCharField(JNIEnv *env, jobject obj, jfieldID fieldID, jchar value);
void SetShortField(JNIEnv *env, jobject obj, jfieldID fieldID, jshort value);
void SetIntField(JNIEnv *env, jobject obj, jfieldID fieldID, jint value);
void SetLongField(JNIEnv *env, jobject obj, jfieldID fieldID, jlong value);
void SetFloatField(JNIEnv *env, jobject obj, jfieldID fieldID, jfloat value);
void SetDoubleField(JNIEnv *env, jobject obj, jfieldID fieldID, jdouble value);
---------------------------
//参数:
env JNI接口指针
objectjava对象(必须不能为 NULL
fieldId :一个有效的fieldID
value:设置的新值

九、调用对象方法

9.1 GetMethodID

返回一个类或接口的非静态实例方法MethodID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
-----------------------------
//参数:
env JNI接口指针
clazzjava class object
name:方法名(MUTF-8编码)
sig:方法签名(MUTF-8编码)

//返回值:
返回方法的MethodID,如果找不到该方法则返回 NULL

//抛出异常:
NoSucMethodError :如果指定的方法没有被找到
ExceptionInInitializerError: 如果在初始化类的过程中出现异常
OutOfMemoryError  如果系统内存不足时。

9.2 CallMethod(MethodA, MethodV)

调用一个Java非静态实例方法,methodID 根据 GetMethodID() 来获取。

如果用这些函数来访问private方法或构造器方法,这MethodID必须来至真正的 obj类,而不能是它们的父类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
void CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
void CallVoidMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jobject CallObjectMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jobject CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jobject CallObjectMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jboolean CallBooleanMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jboolean CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jboolean CallBooleanMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jbyte CallByteMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jbyte CallByteMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jbyte CallByteMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jchar CallCharMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jchar CallCharMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jchar CallCharMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jshort CallShortMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jshort CallShortMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jshort CallShortMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jint CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jint CallIntMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jint CallIntMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jlong CallLongMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jlong CallLongMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jlong CallLongMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jfloat CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jfloat CallFloatMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jfloat CallFloatMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jdouble CallDoubleMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jdouble CallDoubleMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jdouble CallDoubleMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

------------------------------
//参数:
env JNI接口指针
obj :  调用方法的java object
methodID:构造器方法ID
const jvalue *args: 构造器参数
va_list args : 构造器函数

//返回值:
返回Java方法的返回结果。

//抛出异常:
在执行Java方法时可能抛出的任何异常。

9.3 CallNonvirtualMethod(MethodA, MethodV)

CallNonvirtual<type>Method 调用方式是基于 jclass 对象,通常 jobject 传入子类对象, jclass 传入父类class,则可以调用其子类的父类方法.

1
2
3
4
5
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);