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(JNIEnv*, jobject, jmethodID, va_list);
jboolean CallBooleanMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jbyte CallByteMethod(JNIEnv*, jobject, jmethodID, ...);
jbyte CallByteMethodV(JNIEnv*, jobject, jmethodID, va_list);
jbyte CallByteMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jchar CallCharMethod(JNIEnv*, jobject, jmethodID, ...);
jchar CallCharMethodV(JNIEnv*, jobject, jmethodID, va_list);
jchar CallCharMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jshort CallShortMethod(JNIEnv*, jobject, jmethodID, ...);
jshort CallShortMethodV(JNIEnv*, jobject, jmethodID, va_list);
jshort CallShortMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jint CallIntMethod(JNIEnv*, jobject, jmethodID, ...);
jint CallIntMethodV(JNIEnv*, jobject, jmethodID, va_list);
jint CallIntMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jlong CallLongMethod(JNIEnv*, jobject, jmethodID, ...);
jlong CallLongMethodV(JNIEnv*, jobject, jmethodID, va_list);
jlong CallLongMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jfloat CallFloatMethod(JNIEnv*, jobject, jmethodID, ...);
jfloat CallFloatMethodV(JNIEnv*, jobject, jmethodID, va_list);
jfloat CallFloatMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
jdouble CallDoubleMethod(JNIEnv*, jobject, jmethodID, ...);
jdouble CallDoubleMethodV(JNIEnv*, jobject, jmethodID, va_list);
jdouble CallDoubleMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
void CallVoidMethod(JNIEnv*, jobject, jmethodID, ...);
void CallVoidMethodV(JNIEnv*, jobject, jmethodID, va_list);
void CallVoidMethodA(JNIEnv*, jobject, jmethodID, const jvalue*);
// 调用静态方法
jobject CallStaticObjectMethod(JNIEnv*, jclass, jmethodID, ...);
jobject CallStaticObjectMethodV(JNIEnv*, jclass, jmethodID, va_list);
jobject CallStaticObjectMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jboolean CallStaticBooleanMethod(JNIEnv*, jclass, jmethodID, ...);
jboolean CallStaticBooleanMethodV(JNIEnv*, jclass, jmethodID, va_list);
jboolean CallStaticBooleanMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jbyte CallStaticByteMethod(JNIEnv*, jclass, jmethodID, ...);
jbyte CallStaticByteMethodV(JNIEnv*, jclass, jmethodID, va_list);
jbyte CallStaticByteMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jchar CallStaticCharMethod(JNIEnv*, jclass, jmethodID, ...);
jchar CallStaticCharMethodV(JNIEnv*, jclass, jmethodID, va_list);
jchar CallStaticCharMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jshort CallStaticShortMethod(JNIEnv*, jclass, jmethodID, ...);
jshort CallStaticShortMethodV(JNIEnv*, jclass, jmethodID, va_list);
jshort CallStaticShortMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jint CallStaticIntMethod(JNIEnv*, jclass, jmethodID, ...);
jint CallStaticIntMethodV(JNIEnv*, jclass, jmethodID, va_list);
jint CallStaticIntMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jlong CallStaticLongMethod(JNIEnv*, jclass, jmethodID, ...);
jlong CallStaticLongMethodV(JNIEnv*, jclass, jmethodID, va_list);
jlong CallStaticLongMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jfloat CallStaticFloatMethod(JNIEnv*, jclass, jmethodID, ...);
jfloat CallStaticFloatMethodV(JNIEnv*, jclass, jmethodID, va_list);
jfloat CallStaticFloatMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
jdouble CallStaticDoubleMethod(JNIEnv*, jclass, jmethodID, ...);
jdouble CallStaticDoubleMethodV(JNIEnv*, jclass, jmethodID, va_list);
jdouble CallStaticDoubleMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
void CallStaticVoidMethod(JNIEnv*, jclass, jmethodID, ...);
void CallStaticVoidMethodV(JNIEnv*, jclass, jmethodID, va_list);
void CallStaticVoidMethodA(JNIEnv*, jclass, jmethodID, const jvalue*);
在 JNI 中调用 java 对象的变量或者方法时常常会用到 jfieldID 和 jmethodID。
可以看下面的例子:
JNIEXPORT void JNICALL Java_com_sample_MainActivity_stringFromJNI(
JNIEnv* env, jobject this_obj)
{
/* get the class */
jclass class_obj = (*env)->GetObjectClass(env, this_obj);
/* get the field ID */
jfieldID id_age = (*env)->GetFieldID(env, class_obj, "age", "I");
jfieldID id_name = (*env)->GetFieldID(env, class_obj, "name", "Ljava/lang/String;");
/* get the field value */
jint age = (*env)->GetIntField(env, this_obj, id_age);
jstring age = (*env)->GetIntField(env, this_obj, id_age);
age += 1;
/* set the field value */
(*env)->SetIntField(env, this_obj, id_age, age);
jmethodID methodInActivity =
env->GetMethodID(env->GetObjectClass(this_obj), "methodInActivity", "()V");
env->CallVoidMethod(this_obj, methodInActivity);
}
JNI 类和对象
JNI 类
/*
* @brief 定义新的类或接口
*
* @param env: JNI 接口指针.
* @param name: 要定义的类或接口的名称。
* @param loader: 分配给已定义类的类加载器。
* @param buf: 包含 .class 文件数据的缓冲区。
* @param bufLen: 缓冲区长度。
* @return 返回 Java 类。如果发生错误,则返回 NULL。
*/
jclass DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize bufLen);
/*
* @brief 加载一个已经定义过的类
*
* @param env: JNI 接口指针。
* @param name: 完全限定的类名。例如 java.lang.String:java/lang/String。
* 如果名称以"["(数组签名字符)开头,则返回数组类。
* @return 返回 Java 类。如果发生错误,则返回 NULL。
*/
jclass FindClass(JNIEnv *env, const char *name);
/*
* @brief: 加载一个已经定义过的类的父类
*
* @param env: JNI 接口指针。
* @param clazz: 一个 Java 类。
* @return 返回父类。如果发生错误,则返回 NULL。
*/
jclass GetSuperclass(JNIEnv *env, jclass clazz);
/*
* @brief 判断 clazz1 是否可以安全得转换为 clazz2
*
* @param env: JNI 接口指针。
* @param clazz1: 第一个类参数。
* @param clazz2: 第二个类参数。
* @return 如果可以转换,则返回 JNI_TRUE
*/
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1, jclass clazz2);
JNI 对象
/*
* @brief 创建新的 Java 对象,而无需调用该对象的任何构造函数。返回该对象的引用。clazz 参数不能为任何数组类。
*
* @param env: JNI接口指针。
* @param clazz: 一个 Java 类。
* @return 返回 Java 对象,无法构造该对象则返回 NULL。
*/
jobject AllocObject(JNIEnv *env, jclass clazz);
/*
* @brief 创建新的 Java 对象,指定够照方法
*
* @param env: JNI 接口指针。
* @param clazz: 一个 Java 类。
* @param methodID:构造函数的 methodID。必须通过 GetMethodID() 获取构造方法的 methodID。
构造方法名为 <init>,方法签名为 (I)V、()V 等
* @param ...:构造函数的参数。
* @param args:构造函数的参数数组。
* @param args:构造函数参数的 va_list。
* @return 返回 Java 对象,无法构造该对象则返回 NULL。
*/
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
/*
* @brief 获取对象所属的类
*
* @param env: JNI 接口指针。
* @param obj: 一个 Java 对象(必须不是 NULL)。
* @return Java 类。
*/
jclass GetObjectClass(JNIEnv *env, jobject obj);
/*
* @brief 获取 obj 的引用类型。
*
* @param env: JNI 接口指针。
* @param obj: 局部引用、全局引用或者弱全局引用。
* @return 返回以下枚举值之一:
* - 如果 obj 不是有效的引用,则返回 JNIInvalidRefType = 0。
* - 如果 obj 是局部引用类型,则返回 JNILocalRefType = 1。
* - 如果 obj 是全局引用类型,则返回 JNIGlobalRefType = 2。
* - 如果 obj 是弱全局引用类型,则返回 JNIWeakGlobalRefType = 3。
*/
jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);
/*
* @brief 判断对象是否是某个类的实例。
*
* @param env:JNI 接口指针。
* @param obj:一个 Java 对象
* @return JNI_TRUE 或者 JNI_FALSE。
*/
jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
/*
* @brief 判断两个引用是否引用相同的 Java 对象。
*
* @param env: JNI 接口指针。
* @param ref1:一个Java对象。
* @param ref2:一个Java对象。
* @return 如果 ref1 和 ref2 引用相同的 Java 对象,或者两者均为 NULL,返回 JNI_TRUE; 否则返回 JNI_FALSE。
*/
jboolean IsSameObject(JNIEnv *env, jobject ref1, jobject ref2);
JNI 类和对象例子
创建 Java 对象
jclass clazz = env->FindClass("java/lang/Integer");
if (clazz != nullptr) {
jmethodID constructMethodId = env->GetMethodID(clazz, "<init>", "(I)V");
jobject integerObject = env->NewObject(clazz, constructMethodId, jvalue);
}
获取成员变量
extern "C" JNIEXPORT void JNICALL
Java_com_teletian_sample_myndk_MainActivity_testObject(JNIEnv *env, jobject this_obj) {
jclass clazz = env->FindClass("com/teletian/sample/myndk/MainActivity");
if (clazz != nullptr) {
// 对应 MainActivity 中的 public int age = 1;
jfieldID ageFieldId = env->GetFieldID(clazz, "age", "I");
jint age_jint = env->GetIntField(this_obj, ageFieldId);
// 对应 MainActivity 中的 public String name = "Jack";
jfieldID nameFieldId = env->GetFieldID(clazz, "name", "Ljava/lang/String;");
jstring name_jstring = (jstring) env->GetObjectField(this_obj, nameFieldId);
// 从 jsting 获取 C 格式字符串
// 关于 GetStringUTFChars 的详细解释,请参考下文
char *name = (char *) env->GetStringUTFChars(name_jstring, nullptr);
LOGI("name:%s; age:%d", name);
env->ReleaseStringUTFChars(name_jstring, name);
}
// 虽然会自动释放,但是手动释放是个好习惯
env->DeleteLocalRef(clazz);
}
调用对象方法
extern "C" JNIEXPORT void JNICALL
Java_com_teletian_sample_myndk_MainActivity_testObject(JNIEnv *env, jobject this_obj) {
jclass clazz = env->FindClass("com/teletian/sample/myndk/MainActivity");
if (clazz != nullptr) {
// 对应 public int getAge() 方法
jmethodID getAgeMethodId = env->GetMethodID(clazz, "getAge", "()I");
env->CallIntMethod(this_obj, getAgeMethodId);
// 对应 public void printMsg(String msg) 方法
jmethodID printMsgMethodId = env->GetMethodID(clazz, "printMsg", "(Ljava/lang/String;)V");
std::string msg = "age: " + std::to_string(age_jint);
env->CallVoidMethod(this_obj, printMsgMethodId, env->NewStringUTF(msg.c_str()));
}
// 虽然会自动释放,但是手动释放是个好习惯
env->DeleteLocalRef(clazz);
}
静态字段和静态方法
extern "C" JNIEXPORT void JNICALL
Java_com_teletian_sample_myndk_MainActivity_testObject(JNIEnv *env, jobject this_obj) {
jclass clazz = env->FindClass("com/teletian/sample/myndk/MainActivity");
if (clazz != nullptr) {
// 对应 public static String KEY = "key";
jfieldID keyFieldId = env->GetStaticFieldID(clazz, "KEY", "Ljava/lang/String;");
jstring key_jsting = (jstring) env->GetStaticObjectField(clazz, keyFieldId);
// 对应 public static String staticMethod(String name) 方法
jmethodID staticMethodId = env->GetStaticMethodID(
clazz, "staticMethod", "(Ljava/lang/String;)Ljava/lang/String;");
jstring param_jsting = env->NewStringUTF("param");
jstring return_jsting = (jstring) env->CallStaticObjectMethod(
clazz, staticMethodId, param_jsting);
// 从 jsting 获取 C 格式字符串
// 关于 GetStringUTFChars 的详细解释,请参考下文1
const char *key = env->GetStringUTFChars(param_jsting, nullptr);
const char *return_value = env->GetStringUTFChars(return_jsting, nullptr);
LOGI("key:%s; return_value:%s", return_value, return_value);
env->ReleaseStringUTFChars(key_jsting, key);
env->ReleaseStringUTFChars(return_jsting, return_value);
}
// 虽然会自动释放,但是手动释放是个好习惯
env->DeleteLocalRef(clazz);
}
JNI 字符串
NewString
创建 jsting 字符串
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
GetStringLength
获取 jsting 长度
jsize GetStringLength(JNIEnv *env, jstring string);
GetStringChars
jsting转换为 jchar 数组
isCopy 的解释可以参考 GetStringUTFChars 的介绍
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
GetStringChars 获取到的 jchar 数组使用完了要释放
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
GetStringRegion
从字符串中的指定位置复制指定长度的字符到字符数组中
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
NewStringUTF
创建 UTF-8 jsting 字符串
jstring NewStringUTF(JNIEnv *env, const char *bytes);
GetStringUTFLength
获取 UTF-8 jsting 长度
jsize GetStringUTFLength(JNIEnv *env, jstring string);
GetStringUTFChars
从 jstring 中获取 char *
const char* GetStringUTFChars(jstring string, jboolean* isCopy)
isCopy 是一个 jboolean 引用,是作为返回值的,当它返回的值是非 nullptr 时,JNI_TRUE 代表复制了一份,JNI_FALSE 代表没有复制。
这边千万别弄错了,isCopy 不是让你告诉系统需不需要复制的,而是作为返回值让系统告诉你它有没有复制一份。
这个有什么用呢?如果返回 JNI_TRUE 的话,就可以当成一个临时的存储,安全的使用了。
jboolean isCopy;
const char* something=env->GetStringUTFChars(somethingFromJava, &isCopy);
当然,如果对有没有复制不关心,直接传 nullptr 即可
const char* something=env->GetStringUTFChars(somethingFromJava, nullptr);
不管有没有复制一份,使用完了都需要 release。(从 java 中 get 到的类和对象都需要 release)
env->ReleaseStringUTFChars(somethingFromJava, something);
GetStringUTFRegion
从 UTF-8 字符串中的指定位置复制指定长度的字符到字符数组中
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
字符串使用例
extern "C" JNIEXPORT jstring JNICALL Java_com_teletian_sample_myndk_MainActivity_testString(JNIEnv *env,
jobject thiz,
jstring s_jstring) {
char *s = (char *) env->GetStringUTFChars(s_jstring, nullptr);
// string& operator= (const char* s); 复制 s 到 ss
std::string ss = s;
ss.append("\n");
ss.append("append");
env->ReleaseStringUTFChars(s_jstring, s);
return env->NewStringUTF(ss.c_str());
}
JNI 数组
创建 java 侧的数组
jbooleanArray NewBooleanArray(JNIEnv*, jsize);
jbyteArray NewByteArray(JNIEnv*, jsize);
jcharArray NewCharArray(JNIEnv*, jsize);
jshortArray NewShortArray(JNIEnv*, jsize);
jintArray NewIntArray(JNIEnv*, jsize);
jlongArray NewLongArray(JNIEnv*, jsize);
jfloatArray NewFloatArray(JNIEnv*, jsize);
jdoubleArray NewDoubleArray(JNIEnv*, jsize);
/*
* @param elementClass:数组元素类。
* @param initialElement:数组元素初值。
*/
jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
从 java 侧数组获取 C 侧数组
// 基本类型数组获取
// jboolean* isCopy 和上文 GetStringUTFChars 一样
jboolean* GetBooleanArrayElements(JNIEnv*, jbooleanArray, jboolean*);
jbyte* GetByteArrayElements(JNIEnv*, jbyteArray, jboolean*);
jchar* GetCharArrayElements(JNIEnv*, jcharArray, jboolean*);
jshort* GetShortArrayElements(JNIEnv*, jshortArray, jboolean*);
jint* GetIntArrayElements(JNIEnv*, jintArray, jboolean*);
jlong* GetLongArrayElements(JNIEnv*, jlongArray, jboolean*);
jfloat* GetFloatArrayElements(JNIEnv*, jfloatArray, jboolean*);
jdouble* GetDoubleArrayElements(JNIEnv*, jdoubleArray, jboolean*);
// 获取指定范围
void GetBooleanArrayRegion(JNIEnv*, jbooleanArray, jsize, jsize, jboolean*);
void GetByteArrayRegion(JNIEnv*, jbyteArray, jsize, jsize, jbyte*);
void GetCharArrayRegion(JNIEnv*, jcharArray, jsize, jsize, jchar*);
void GetShortArrayRegion(JNIEnv*, jshortArray, jsize, jsize, jshort*);
void GetIntArrayRegion(JNIEnv*, jintArray, jsize, jsize, jint*);
void GetLongArrayRegion(JNIEnv*, jlongArray, jsize, jsize, jlong*);
void GetFloatArrayRegion(JNIEnv*, jfloatArray, jsize, jsize, jfloat*);
void GetDoubleArrayRegion(JNIEnv*, jdoubleArray, jsize, jsize, jdouble*);
// 引用类型数组获取
// 因为基本类型 C/C++ 里面也有,所以可以直接用数组去接,而引用类型 C/C++ 里面没有,所以只能传入索引一个一个获取
// 获取的还是 java 对象 jobject,所以不需要释放
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
设置数组值
// 基本类型(只有指定范围的版本)
void SetBooleanArrayRegion(JNIEnv*, jbooleanArray, jsize, jsize, const jboolean*);
void SetByteArrayRegion(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*);
void SetCharArrayRegion(JNIEnv*, jcharArray, jsize, jsize, const jchar*);
void SetShortArrayRegion(JNIEnv*, jshortArray, jsize, jsize, const jshort*);
void SetIntArrayRegion(JNIEnv*, jintArray, jsize, jsize, const jint*);
void SetLongArrayRegion(JNIEnv*, jlongArray, jsize, jsize, const jlong*);
void SetFloatArrayRegion(JNIEnv*, jfloatArray, jsize, jsize, const jfloat*);
void SetDoubleArrayRegion(JNIEnv*, jdoubleArray, jsize, jsize, const jdouble*);
// 引用类型
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
使用完了要释放(在释放之前,C 中的操作不会影响 Java 的原数组,但是一旦释放了,C 中所做的修改会反应到 Java 原数组)
void ReleaseBooleanArrayElements(JNIEnv*, jbooleanArray, jboolean*, jint);
void ReleaseByteArrayElements(JNIEnv*, jbyteArray, jbyte*, jint);
void ReleaseCharArrayElements(JNIEnv*, jcharArray, jchar*, jint);
void ReleaseShortArrayElements(JNIEnv*, jshortArray, jshort*, jint);
void ReleaseIntArrayElements(JNIEnv*, jintArray, jint*, jint);
void ReleaseLongArrayElements(JNIEnv*, jlongArray, jlong*, jint);
void ReleaseFloatArrayElements(JNIEnv*, jfloatArray, jfloat*, jint);
void ReleaseDoubleArrayElements(JNIEnv*, jdoubleArray, jdouble*, jint);
// 引用类型数组不需要 release,原因上文的创建数组部分已经讲过。但是需要 deleteLocalRef
获取数组长度
jsize GetArrayLength(JNIEnv *env, jarray array);
数组使用例
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
int[] arr1 = {1, 1};
String[] arr2 = {"java value"};
String s = "result:" + Arrays.toString(testArray(arr1, arr2))
+ "\n" + "arr1:" + Arrays.toString(arr1)
+ "\n" + "arr2:" + Arrays.toString(arr2);
tv.setText(s);
}
public native int[] testArray(int[] arr1, String[] arr2);
native-lib.cpp
extern "C" JNIEXPORT jintArray JNICALL
Java_com_teletian_sample_myndk_MainActivity_testArray(JNIEnv *env,
jobject thiz,
jintArray arr1,
jobjectArray arr2) {
// 基本类型数组
jint* _arr1 = env->GetIntArrayElements(arr1, nullptr);
int length1 = env->GetArrayLength(arr1);
for (int i = 0; i < length1; i++) {
_arr1[i] = 2; // 修改数组值。在这里,只修改了 _aar1 的值,aar1 的值不变
}
// 一旦 ReleaseIntArrayElements 调用了,对 _aar1 的修改会反应到原数组 aar1 中去
env->ReleaseIntArrayElements(arr1, _arr1, 0);
// 引用数组,只能根据 index 取单个值
jstring _arr2 = (jstring) env->GetObjectArrayElement(arr2, 0);
const char* s = env->GetStringUTFChars(_arr2, nullptr);
LOGD("[testArray] old arr2[0]:%s", s);
jstring newArr2 = env->NewStringUTF("JNI value");
env->SetObjectArrayElement(arr2, 0, newArr2);
// create new array
int array[2] = {3, 3};
jintArray dst = env->NewIntArray(2);
env->SetIntArrayRegion(dst, 0, 2, array);
return dst;
}
网友评论