JNI 类型
JNI 中有许多和 Java 相对应的类型
Java 类型 | JNI 类型 |
---|---|
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
void | void |
java.lang.Class | jclass |
java.lang.String | jstring |
java.lang.Throwable | jthrowable |
object | jobject |
object[] | jobjectarray |
boolean[] | jbooleanarray |
byte[] | jbytearray |
char[] | jchararray |
short[] | jshortarray |
int[] | jintarray |
long[] | jlongarray |
float[] | jfloatarray |
double[] | jdoublearray |
类型签名
Java 类型 | JNI 类型签名 |
---|---|
void | V |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
L fully-qualified-class ; | fully-qualified-class |
type[] | [ type |
method type | ( arg-types ) ret-type |
例如,下面的 Java 方法:
public long foo(int n, String s, int[] arr)
在 JNI 中的签名如下:
(ILjava/lang/String;[I)J
JNI 引用
JNI 定义了八种 Java 基本类型,其余的 jobject、jclass、jarray、jxxxArray、jstring 等都是引用类型。
JNI 的引用有两层含义:
- Java 中的引用类型
- C/C++ 中的指针
但是如果引用被 JVM 释放了,指针仍然指向一个地址,只是对应的地址中数据已经被释放了
JNI 的引用分为四种:
-
全局引用(GlobalReferences):全局有效。JVM 无法释放回收,必须通过调用 DeleteGlobalRef() 显式释放。
创建全局引用:jobject NewGlobalRef(JNIEnv *env, jobject obj);
释放全局引用:void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
-
弱全局引用(WeakGlobalReferences):一种特殊的全局引用,可以被 JVM 回收。
创建弱全局引用:jobject NewWeakGlobalRef(JNIEnv *env, jobject obj);
释放弱全局引用:void DeleteWeakGlobalRef(JNIEnv *env, jobject globalRef);
-
局部引用(LocalReferences):在方法内创建,方法结束后自动释放。虽然会在方法结束后自动释放,但是如果消耗过多 JVM 资源,也可以手动释放。
创建局部引用:jobject NewLocalRef(JNIEnv *env, jobject obj);
释放局部引用:void DeleteLocalRef(JNIEnv *env, jobject globalRef);
虽然方法结束会自动释放,但是建议使用完了就手动释放。尤其以下两种情况必须手动释放:
- 引用一个很大的 Java 对象
- 在 for 循环中创建了大量的引用。引用多了之后会报 ReferenceTable overflow 异常。
哪些场景需要释放?JNI 函数内部创建的 jobject、jclass、jstring、jarray 等引用都需要释放。
- FindClass / DeleteLocalRef
- NewString / DeleteLocalRef
- NewStringUTF / DeleteLocalRef
- NewObject / DeleteLocalRef
- NewXxxArray / DeleteLocalRef
- GetObjectField / DeleteLocalRef
- GetObjectClass / DeleteLocalRef
- GetObjectArrayElement / DeleteLocalRef
- 注意:对于 GetStringChars、GetStringUTFChars、GetXxxArrayElements 基本类型数组,需要调用对应的 Release 方法去释放本地内存
-
无效引用(InvalidReferences):无效引用一般情况下没有什么用,不展开介绍。
字段和方法 ID
jfieldID 和 jmethodID 是常规的 C 指针类型,它们的声明如下:
struct _jfieldID; /* opaque structure */
typedef struct _jfieldID *jfieldID; /* field IDs */
struct _jmethodID; /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */
GetFieldID / GetXxxField / SetXxxField
GetStaticFieldID / GetStaticXxxField / SetStaticXxxField
/*
* @param env: JN I接口指针。
* @param clazz:一个 Java 类。
* @param name:字段名称,以 \0 结尾的 UTF-8 字符串。
* @param sig:字段签名,以 \0 结尾的 UTF-8 字符串。
* @return 返回字段 ID,如果操作失败返回 NULL。
*/
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
// 获取静态字段
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jobject GetObjectField(JNIEnv*, jobject, jfieldID);
jboolean GetBooleanField(JNIEnv*, jobject, jfieldID);
jbyte GetByteField(JNIEnv*, jobject, jfieldID);
jchar GetCharField(JNIEnv*, jobject, jfieldID);
jshort GetShortField(JNIEnv*, jobject, jfieldID);
jint GetIntField(JNIEnv*, jobject, jfieldID);
jlong GetLongField(JNIEnv*, jobject, jfieldID);
jfloat GetFloatField(JNIEnv*, jobject, jfieldID);
jdouble GetDoubleField(JNIEnv*, jobject, jfieldID);
// 获取静态字段值
jobject GetStaticObjectField(JNIEnv*, jclass, jfieldID);
jboolean GetStaticBooleanField(JNIEnv*, jclass, jfieldID);
jbyte GetStaticByteField(JNIEnv*, jclass, jfieldID);
jchar GetStaticCharField(JNIEnv*, jclass, jfieldID);
jshort GetStaticShortField(JNIEnv*, jclass, jfieldID);
jint GetStaticIntField(JNIEnv*, jclass, jfieldID);
jlong GetStaticLongField(JNIEnv*, jclass, jfieldID);
jfloat GetStaticFloatField(JNIEnv*, jclass, jfieldID);
jdouble GetStaticDoubleField(JNIEnv*, jclass, jfieldID);
void SetObjectField(JNIEnv*, jobject, jfieldID, jobject);
void SetBooleanField(JNIEnv*, jobject, jfieldID, jboolean);
void SetByteField(JNIEnv*, jobject, jfieldID, jbyte);
void SetCharField(JNIEnv*, jobject, jfieldID, jchar);
void SetShortField(JNIEnv*, jobject, jfieldID, jshort);
void SetIntField(JNIEnv*, jobject, jfieldID, jint);
void SetLongField(JNIEnv*, jobject, jfieldID, jlong);
void SetFloatField(JNIEnv*, jobject, jfieldID, jfloat);
void SetDoubleField(JNIEnv*, jobject, jfieldID, jdouble);
// 设置静态字段值
void SetStaticObjectField(JNIEnv*, jclass, jfieldID, jobject);
void SetStaticBooleanField(JNIEnv*, jclass, jfieldID, jboolean);
void SetStaticByteField(JNIEnv*, jclass, jfieldID, jbyte);
void SetStaticCharField(JNIEnv*, jclass, jfieldID, jchar);
void SetStaticShortField(JNIEnv*, jclass, jfieldID, jshort);
void SetStaticIntField(JNIEnv*, jclass, jfieldID, jint);
void SetStaticLongField(JNIEnv*, jclass, jfieldID, jlong);
void SetStaticFloatField(JNIEnv*, jclass, jfieldID, jfloat);
void SetStaticDoubleField(JNIEnv*, jclass, jfieldID, jdouble);
GetMethodID / CallXxxMethod
GetStaticMethodID / CallStaticXxxMethod
/*
* @param env: JNI 接口指针。
* @param clazz:一个 Java 类。
* @param name:方法名称,以 \0 结尾的 UTF-8 字符串。
* @param sig:方法签名,以 \0 结尾的 UTF-8 字符串。
* @return 返回方法 ID,如果操作失败返回 NULL。
*/
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
// 获取静态方法
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
/*
* 调用实例方法
*
* @param env: JNI 接口指针。
* @param jobject: 一个 Java 对象。
* @param methodID:java 函数的 methodID, 必须通过调用 GetMethodID() 来获得。
* @param ...:java 函数的参数。
* @param args:java 函数的参数数组。
* @param args:java 函数参数的 va_list。
* @return 返回 Java 对象,无法构造该对象则返回 NULL。
*/
jobject CallObjectMethod(JNIEnv*, jobject, jmethodID, ...);
jobject CallObjectMethodV(JNIEnv*, jobject, jmethodID, va_list);
jobject CallObjectMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jboolean CallBooleanMethod(JNIEnv*, jobject, jmethodID, ...);
jboolean CallBooleanMethodV