JNI 定義完整類型對應,確保 Java 與 C/C++ 數據無損傳遞:
| Java 類型 | JNI 類型 | 位寬/簽名 |
|---|---|---|
boolean | jboolean | 8 位,無符號 |
byte | jbyte | 8 位 |
char | jchar | 16 位,無符號 |
short | jshort | 16 位 |
int | jint | 32 位 |
long | jlong | 64 位 |
float | jfloat | 32 位 |
double | jdouble | 64 位 |
void | void | N/A |
基本類型直接值傳遞,無需轉換。jboolean 在 C 中常用 unsigned char 實現。
所有 Java 物件引用統一對應 jobject(相當於 java.lang.Object)。JNI 提供子類型區分常用類別:
jclass、jstring、jarray、jobjectArray、jthrowablejbooleanArray、jbyteArray、jcharArray、jshortArray、jintArray、jlongArray、jfloatArray、jdoubleArrayjstring ≠ C char*,必須透過 JNIEnv 方法轉換:
| 功能 | UTF-8 版本 | Unicode 版本 |
|---|---|---|
| 轉換為 C 字串 | GetStringUTFChars | GetStringChars |
| 釋放 C 字串 | ReleaseStringUTFChars | ReleaseStringChars |
| 建立 Java 字串 | NewStringUTF | NewString |
| 獲取長度 | GetStringUTFLength | GetStringLength |
JNIEXPORT jstring JNICALL Java_Prompt_getLine
(JNIEnv *env, jobject obj, jstring prompt) {
char buf[128];
const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str); // 必須釋放,否則記憶體洩漏
fgets(buf, 128, stdin);
return (*env)->NewStringUTF(env, buf);
}
鐵律: 每個 Get* 必配對 Release*,否則 JVM 無法回收臨時字串緩衝區,導致記憶體洩漏。
JNIEXPORT jint JNICALL Java_IntArray_sumArray
(JNIEnv *env, jobject obj, jintArray arr) {
jsize len = (*env)->GetArrayLength(env, arr); // 獲取長度
jint *body = (*env)->GetIntArrayElements(env, arr, 0); // 獲取元素指針
jint sum = 0;
for (int i = 0; i < len; i++) sum += body[i];
(*env)->ReleaseIntArrayElements(env, arr, body, 0); // 釋放,模式 0 表示複製回 Java 陣列
return sum;
}
| 陣列類型 | Get 函數 | Release 函數 |
|---|---|---|
boolean[] | GetBooleanArrayElements | ReleaseBooleanArrayElements |
byte[] | GetByteArrayElements | ReleaseByteArrayElements |
char[] | GetCharArrayElements | ReleaseCharArrayElements |
short[] | GetShortArrayElements | ReleaseShortArrayElements |
int[] | GetIntArrayElements | ReleaseIntArrayElements |
long[] | GetLongArrayElements | ReleaseLongArrayElements |
float[] | GetFloatArrayElements | ReleaseFloatArrayElements |
double[] | GetDoubleArrayElements | ReleaseDoubleArrayElements |
Object[])無法一次性獲取所有元素,需逐個存取:
GetObjectArrayElement(env, arr, index) → 返回 jobjectSetObjectArrayElement(env, arr, index, val) → 更新元素jclass cls = (*env)->GetObjectClass(env, obj);jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");(*env)->CallVoidMethod(env, obj, mid, depth);格式:(參數類型簽名)返回類型簽名
| Java 類型 | 簽名字符 |
|---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
類別 Lpkg/Cls; | Lpkg/Cls; |
陣列 [type | [I 等 |
範例:void callback(int depth) → (I)V;String toString() → ()Ljava/lang/String;
獲取簽名神器:javap -s -p ClassName
| 返回類型 | 一般調用 | 可變參數 | 陣列參數 |
|---|---|---|---|
void | CallVoidMethod | CallVoidMethodV | CallVoidMethodA |
boolean | CallBooleanMethod | CallBooleanMethodV | CallBooleanMethodA |
int | CallIntMethod | CallIntMethodV | CallIntMethodA |
| ... 其他基本類型同理 | |||
| 物件 | CallObjectMethod | CallObjectMethodV | CallObjectMethodA |
將 GetMethodID → GetStaticMethodID,Call*Method → CallStatic*Method。
與方法調用類比,將 Method 替換為 Field:
GetFieldID / GetStaticFieldIDGetIntField、SetIntField 等類型專用 Get/SetGetObjectField / SetObjectField 處理引用類型JNI 核心在於:類型映射精確、資源獲取與釋放配對、簽名正確、引用管理規範。掌握字串、陣列、方法、欄位四大操作族群,即可應對絕大多數 Java-C/C++ 互操作場景。建議搭配 javah(JDK 8 前)或 javac -h(JDK 8+)自動生成頭文件,減少手寫錯誤。