Android NDK(二)- JNI 基础

这篇博客详细介绍了JNI的基础知识,包括JNI类型、类型签名、引用管理、字段和方法ID、JNI字符串操作以及JNI数组的使用。重点讨论了JNI引用的四种类型:全局引用、弱全局引用、局部引用和无效引用,以及何时需要释放这些引用。同时,讲解了如何创建和操作Java对象、字符串和数组,并给出了实例代码。
摘要由CSDN通过智能技术生成

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 的引用有两层含义:

  1. Java 中的引用类型
  2. C/C++ 中的指针

但是如果引用被 JVM 释放了,指针仍然指向一个地址,只是对应的地址中数据已经被释放了

JNI 的引用分为四种:

  1. 全局引用(GlobalReferences):全局有效。JVM 无法释放回收,必须通过调用 DeleteGlobalRef() 显式释放。
    创建全局引用:jobject NewGlobalRef(JNIEnv *env, jobject obj);
    释放全局引用:void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

  2. 弱全局引用(WeakGlobalReferences):一种特殊的全局引用,可以被 JVM 回收。
    创建弱全局引用:jobject NewWeakGlobalRef(JNIEnv *env, jobject obj);
    释放弱全局引用:void DeleteWeakGlobalRef(JNIEnv *env, jobject globalRef);

  3. 局部引用(LocalReferences):在方法内创建,方法结束后自动释放。虽然会在方法结束后自动释放,但是如果消耗过多 JVM 资源,也可以手动释放。
    创建局部引用:jobject NewLocalRef(JNIEnv *env, jobject obj);
    释放局部引用:void DeleteLocalRef(JNIEnv *env, jobject globalRef);

    虽然方法结束会自动释放,但是建议使用完了就手动释放。尤其以下两种情况必须手动释放:

    1. 引用一个很大的 Java 对象
    2. 在 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 方法去释放本地内存

  4. 无效引用(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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值