JNI 類型
JNI 中有許多和 Java 相對應(yīng)的類型
| 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 |
關(guān)于 JNI 中基本數(shù)據(jù)類型和 C++ 中的基本數(shù)據(jù)類型,它們是可以直接互相轉(zhuǎn)化的,不需要特別的操作。參照 jni.h 文件可以發(fā)現(xiàn),其實就是換了個名字而已,其實本質(zhì)是一個東西。
/* Primitive types that match up with Java equivalents. */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
/* "cardinal indices and sizes" */
typedef jint jsize;
類型簽名
| 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 釋放了,指針仍然指向一個地址,只是對應(yīng)的地址中數(shù)據(jù)已經(jīng)被釋放了
JNI 的引用分為四種:
全局引用(GlobalReferences):全局有效。JVM 無法釋放回收,必須通過調(diào)用 DeleteGlobalRef() 顯式釋放。
創(chuàng)建全局引用:jobject NewGlobalRef(JNIEnv *env, jobject obj);
釋放全局引用:void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
弱全局引用(WeakGlobalReferences):一種特殊的全局引用,可以被 JVM 回收。
創(chuàng)建弱全局引用:jobject NewWeakGlobalRef(JNIEnv *env, jobject obj);
釋放弱全局引用:void DeleteWeakGlobalRef(JNIEnv *env, jobject globalRef);
-
局部引用(LocalReferences):在方法內(nèi)創(chuàng)建,方法結(jié)束后自動釋放。雖然會在方法結(jié)束后自動釋放,但是如果消耗過多 JVM 資源,也可以手動釋放。
創(chuàng)建局部引用:jobject NewLocalRef(JNIEnv *env, jobject obj);
釋放局部引用:void DeleteLocalRef(JNIEnv *env, jobject globalRef);雖然方法結(jié)束會自動釋放,但是建議使用完了就手動釋放。尤其以下兩種情況必須手動釋放:
- 引用一個很大的 Java 對象
- 在 for 循環(huán)中創(chuàng)建了大量的引用。引用多了之后會報 ReferenceTable overflow 異常。
哪些場景需要釋放?JNI 函數(shù)內(nèi)部創(chuàng)建的 jobject、jclass、jstring、jarray 等引用都需要釋放。
- FindClass / DeleteLocalRef
- NewString / DeleteLocalRef
- NewStringUTF / DeleteLocalRef
- NewObject / DeleteLocalRef
- NewXxxArray / DeleteLocalRef
- GetObjectField / DeleteLocalRef
- GetObjectClass / DeleteLocalRef
- GetObjectArrayElement / DeleteLocalRef
- 注意:對于 GetStringChars、GetStringUTFChars、GetXxxArrayElements 基本類型數(shù)組,需要調(diào)用對應(yīng)的 Release 方法去釋放本地內(nèi)存
無效引用(InvalidReferences):無效引用一般情況下沒有什么用,不展開介紹。
字段和方法 ID
jfieldID 和 jmethodID 是常規(guī)的 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 結(jié)尾的 UTF-8 字符串。
* @param sig:字段簽名,以 \0 結(jié)尾的 UTF-8 字符串。
* @return 返回字段 ID,如果操作失敗返回 NULL。
*/
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
// 獲取靜態(tài)字段
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);
// 獲取靜態(tài)字段值
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);
// 設(shè)置靜態(tài)字段值
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 結(jié)尾的 UTF-8 字符串。
* @param sig:方法簽名,以 \0 結(jié)尾的 UTF-8 字符串。
* @return 返回方法 ID,如果操作失敗返回 NULL。
*/
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
// 獲取靜態(tài)方法
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
/*
* 調(diào)用實例方法
*
* @param env: JNI 接口指針。
* @param jobject: 一個 Java 對象。
* @param methodID:java 函數(shù)的 methodID, 必須通過調(diào)用 GetMethodID() 來獲得。
* @param ...:java 函數(shù)的參數(shù)。
* @param args:java 函數(shù)的參數(shù)數(shù)組。
* @param args:java 函數(shù)參數(shù)的 va_list。
* @return 返回 Java 對象,無法構(gòu)造該對象則返回 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*);
// 調(diào)用靜態(tài)方法
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 中調(diào)用 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 文件數(shù)據(jù)的緩沖區(qū)。
* @param bufLen: 緩沖區(qū)長度。
* @return 返回 Java 類。如果發(fā)生錯誤,則返回 NULL。
*/
jclass DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize bufLen);
/*
* @brief 加載一個已經(jīng)定義過的類
*
* @param env: JNI 接口指針。
* @param name: 完全限定的類名。例如 java.lang.String:java/lang/String。
* 如果名稱以"["(數(shù)組簽名字符)開頭,則返回數(shù)組類。
* @return 返回 Java 類。如果發(fā)生錯誤,則返回 NULL。
*/
jclass FindClass(JNIEnv *env, const char *name);
/*
* @brief: 加載一個已經(jīng)定義過的類的父類
*
* @param env: JNI 接口指針。
* @param clazz: 一個 Java 類。
* @return 返回父類。如果發(fā)生錯誤,則返回 NULL。
*/
jclass GetSuperclass(JNIEnv *env, jclass clazz);
/*
* @brief 判斷 clazz1 是否可以安全得轉(zhuǎn)換為 clazz2
*
* @param env: JNI 接口指針。
* @param clazz1: 第一個類參數(shù)。
* @param clazz2: 第二個類參數(shù)。
* @return 如果可以轉(zhuǎn)換,則返回 JNI_TRUE
*/
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1, jclass clazz2);
JNI 對象
/*
* @brief 創(chuàng)建新的 Java 對象,而無需調(diào)用該對象的任何構(gòu)造函數(shù)。返回該對象的引用。clazz 參數(shù)不能為任何數(shù)組類。
*
* @param env: JNI接口指針。
* @param clazz: 一個 Java 類。
* @return 返回 Java 對象,無法構(gòu)造該對象則返回 NULL。
*/
jobject AllocObject(JNIEnv *env, jclass clazz);
/*
* @brief 創(chuàng)建新的 Java 對象,指定夠照方法
*
* @param env: JNI 接口指針。
* @param clazz: 一個 Java 類。
* @param methodID:構(gòu)造函數(shù)的 methodID。必須通過 GetMethodID() 獲取構(gòu)造方法的 methodID。
構(gòu)造方法名為 <init>,方法簽名為 (I)V、()V 等
* @param ...:構(gòu)造函數(shù)的參數(shù)。
* @param args:構(gòu)造函數(shù)的參數(shù)數(shù)組。
* @param args:構(gòu)造函數(shù)參數(shù)的 va_list。
* @return 返回 Java 對象,無法構(gòu)造該對象則返回 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 類和對象例子
創(chuàng)建 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) {
// 對應(yīng) MainActivity 中的 public int age = 1;
jfieldID ageFieldId = env->GetFieldID(clazz, "age", "I");
jint age_jint = env->GetIntField(this_obj, ageFieldId);
// 對應(yīng) 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 格式字符串
// 關(guān)于 GetStringUTFChars 的詳細解釋,請參考下文
char *name = (char *) env->GetStringUTFChars(name_jstring, nullptr);
LOGI("name:%s; age:%d", name);
env->ReleaseStringUTFChars(name_jstring, name);
}
// 雖然會自動釋放,但是手動釋放是個好習(xí)慣
env->DeleteLocalRef(clazz);
}
調(diào)用對象方法
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) {
// 對應(yīng) public int getAge() 方法
jmethodID getAgeMethodId = env->GetMethodID(clazz, "getAge", "()I");
env->CallIntMethod(this_obj, getAgeMethodId);
// 對應(yīng) 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()));
}
// 雖然會自動釋放,但是手動釋放是個好習(xí)慣
env->DeleteLocalRef(clazz);
}
靜態(tài)字段和靜態(tài)方法
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) {
// 對應(yīng) public static String KEY = "key";
jfieldID keyFieldId = env->GetStaticFieldID(clazz, "KEY", "Ljava/lang/String;");
jstring key_jsting = (jstring) env->GetStaticObjectField(clazz, keyFieldId);
// 對應(yīng) 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 格式字符串
// 關(guān)于 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);
}
// 雖然會自動釋放,但是手動釋放是個好習(xí)慣
env->DeleteLocalRef(clazz);
}
JNI 字符串
NewString
創(chuàng)建 jsting 字符串
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);
GetStringLength
獲取 jsting 長度
jsize GetStringLength(JNIEnv *env, jstring string);
GetStringChars
jsting轉(zhuǎn)換為 jchar 數(shù)組
isCopy 的解釋可以參考 GetStringUTFChars 的介紹
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
GetStringChars 獲取到的 jchar 數(shù)組使用完了要釋放
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
GetStringRegion
從字符串中的指定位置復(fù)制指定長度的字符到字符數(shù)組中
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
NewStringUTF
創(chuàng)建 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 代表復(fù)制了一份,JNI_FALSE 代表沒有復(fù)制。
這邊千萬別弄錯了,isCopy 不是讓你告訴系統(tǒng)需不需要復(fù)制的,而是作為返回值讓系統(tǒng)告訴你它有沒有復(fù)制一份。
這個有什么用呢?如果返回 JNI_TRUE 的話,就可以當成一個臨時的存儲,安全的使用了。
jboolean isCopy;
const char* something=env->GetStringUTFChars(somethingFromJava, &isCopy);
當然,如果對有沒有復(fù)制不關(guān)心,直接傳 nullptr 即可
const char* something=env->GetStringUTFChars(somethingFromJava, nullptr);
不管有沒有復(fù)制一份,使用完了都需要 release。(從 java 中 get 到的類和對象都需要 release)
env->ReleaseStringUTFChars(somethingFromJava, something);
GetStringUTFRegion
從 UTF-8 字符串中的指定位置復(fù)制指定長度的字符到字符數(shù)組中
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); 復(fù)制 s 到 ss
std::string ss = s;
ss.append("\n");
ss.append("append");
env->ReleaseStringUTFChars(s_jstring, s);
return env->NewStringUTF(ss.c_str());
}
JNI 數(shù)組
創(chuàng)建 java 側(cè)的數(shù)組
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:數(shù)組元素類。
* @param initialElement:數(shù)組元素初值。
*/
jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
從 java 側(cè)數(shù)組獲取 C 側(cè)數(shù)組
// 基本類型數(shù)組獲取
// 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*);
// 引用類型數(shù)組獲取
// 因為基本類型 C/C++ 里面也有,所以可以直接用數(shù)組去接,而引用類型 C/C++ 里面沒有,所以只能傳入索引一個一個獲取
// 獲取的還是 java 對象 jobject,所以不需要釋放
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
設(shè)置數(shù)組值
// 基本類型(只有指定范圍的版本)
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 的原數(shù)組,但是一旦釋放了,C 中所做的修改會反應(yīng)到 Java 原數(shù)組)
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);
// 引用類型數(shù)組不需要 release,原因上文的創(chuàng)建數(shù)組部分已經(jīng)講過。但是需要 deleteLocalRef
獲取數(shù)組長度
jsize GetArrayLength(JNIEnv *env, jarray array);
數(shù)組使用例
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) {
// 基本類型數(shù)組
jint* _arr1 = env->GetIntArrayElements(arr1, nullptr);
int length1 = env->GetArrayLength(arr1);
for (int i = 0; i < length1; i++) {
_arr1[i] = 2; // 修改數(shù)組值。在這里,只修改了 _aar1 的值,aar1 的值不變
}
// 一旦 ReleaseIntArrayElements 調(diào)用了,對 _aar1 的修改會反應(yīng)到原數(shù)組 aar1 中去
env->ReleaseIntArrayElements(arr1, _arr1, 0);
// 引用數(shù)組,只能根據(jù) 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;
}