JNI系列文章:
JNI系列之入門(mén)Hello JNI C(一)
JNI系列之入門(mén)Hello JNI C(二)
JNI系列入門(mén)之C語(yǔ)言與Java的雙向通信(一)
JNI系列入門(mén)之C語(yǔ)言與Java的雙向通信(二)
JNI系列入門(mén)之C語(yǔ)言中文字符串亂碼問(wèn)題
一欢顷、概述
- Java層向C層通信
- 通過(guò)調(diào)用靜態(tài)無(wú)參數(shù)懂酱、有參數(shù)的native方法
- 通過(guò)調(diào)用非靜態(tài)無(wú)參數(shù)、有參數(shù)的native方法
- C層向Java層通信
- C層訪問(wèn)和修改Java層的屬性
- C層訪問(wèn)和修改Java層的靜態(tài)屬性
- C層訪問(wèn)Java層的方法
- C層訪問(wèn)Java層的靜態(tài)方法
- C層訪問(wèn)Java層的構(gòu)造方法洲胖,并創(chuàng)建Java對(duì)象返回
- java中傳入數(shù)組
- C中生成一個(gè)數(shù)組返回給java
二、實(shí)現(xiàn)
Java層向C層通信
- 通過(guò)調(diào)用靜態(tài)無(wú)參數(shù)珊豹、有參數(shù)的native方法
JniTest.java
// native的靜態(tài)方法焙糟,生成的JNI函數(shù)參數(shù)是(JNIEnv *env, jclass jcls)
public native static String getStringFromC();
// 生成的JNI函數(shù)參數(shù)是(JNIEnv *env, jclass jcls, jstring jstr_input)
public native static String getNewString(String input);
在java中聲明兩個(gè)native方法额港,然后通過(guò)javah命令生成頭文件御铃,具體的頭文件生成步驟,可以看JNI系列之入門(mén)Hello JNI C(一):
com_jerry_jnitest_JniTest.h
JNIEXPORT jstring JNICALL Java_com_jerry_jnitest_JniTest_getStringFromC
(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_com_jerry_jnitest_JniTest_getNewString
(JNIEnv *, jclass, jstring);
具體實(shí)現(xiàn):
// 靜態(tài)無(wú)參函數(shù)實(shí)現(xiàn)沈矿,返回一個(gè)C的字符串
JNIEXPORT jstring JNICALL Java_com_jerry_jnitest_JniTest_getStringFromC
(JNIEnv *env, jclass jcls) {
char *text = "Hi, Jerry! 動(dòng)態(tài)鏈接庫(kù)上真,調(diào)用起來(lái)了!";
return (*env)->NewStringUTF(env, text);
}
// 靜態(tài)有參函數(shù)實(shí)現(xiàn)羹膳,新建一個(gè)C字符串與java輸入的字符串拼接后返回給java
JNIEXPORT jstring JNICALL Java_com_jerry_jnitest_JniTest_getNewString
(JNIEnv *env, jclass jcls, jstring jstr_input) {
// 將java的輸入?yún)?shù)jstring -> c字符串
char *input = (*env)->GetStringUTFChars(env, jstr_input, NULL);
char text[30] = "Jerry";
// 拼接字符串
char *new_text = strcat(text, input);
printf("newText = %s\n", new_text);
// 創(chuàng)建一個(gè)jstring返回給java
jstring jstr_new = (*env)->NewStringUTF(env, new_text);
return jstr_new;
}
getStringFromC函數(shù)中谷羞,我們使用(*env)來(lái)調(diào)用NewStringUTF函數(shù)通過(guò)傳入C字符指針也就是字符串來(lái)創(chuàng)建一個(gè)jstring類型的字符串(對(duì)應(yīng)java數(shù)據(jù)類型的String),很簡(jiǎn)單溜徙。也許你會(huì)問(wèn)湃缎,為什么是(*env)來(lái)調(diào)用函數(shù),因?yàn)镴NIEnv本身就是一個(gè)結(jié)構(gòu)體指針了蠢壹,所以env就是結(jié)構(gòu)體二級(jí)指針變量嗓违,而NewStringUTF函數(shù)又是定義在JNIEnv這個(gè)結(jié)構(gòu)體指針的結(jié)構(gòu)體里的函數(shù)(這是一個(gè)函數(shù)指針),所以需要用(*env)來(lái)取到結(jié)構(gòu)體一級(jí)指針變量的地址图贸,來(lái)獲取其地址所指向的內(nèi)容蹂季。具體可以看上一篇文章JNI系列之入門(mén)Hello JNI C(二)
getNewString函數(shù)中多了一個(gè)參數(shù),jstr_input表示java中native方法的參數(shù)String input疏日,在函數(shù)實(shí)現(xiàn)里偿洁,先將jstring轉(zhuǎn)換成c字符串,然后引入c的函數(shù)庫(kù)string.h頭文件沟优,使用strcat拼接出一個(gè)新的c字符串涕滋,然后再用NewStringUTF函數(shù)把新的C字符串->jstring,返回給java挠阁。
public static void main(String[] args) {
// 在main方法中調(diào)用native方法
System.out.println(getStringFromC());
System.out.println(getNewString("真是太帥了..."));
}
輸出結(jié)果:
這是java中的native方法調(diào)用宾肺,看的出來(lái),C的中文字符串亂碼侵俗,而java傳入的中文字符串不會(huì)亂碼锨用。關(guān)于C的中文字符串亂碼問(wèn)題,我將會(huì)在下一篇文章里說(shuō)明解決方案隘谣。
- 通過(guò)調(diào)用非靜態(tài)無(wú)參數(shù)增拥、有參數(shù)的native方法
// native的對(duì)象方法,生成的NI函數(shù)參數(shù)是(JNIEnv *env, jobject jobj)
public native String getNameFromC();
C中的頭文件實(shí)現(xiàn):
// 非靜態(tài)無(wú)參函數(shù)
JNIEXPORT jstring JNICALL Java_com_jerry_jnitest_JniTest_getNameFromC
(JNIEnv *env, jobject jobj) {
return (*env)->NewStringUTF(env, "call getNameFromC");
}
對(duì)于非靜態(tài)native方法寻歧,就是函數(shù)的參數(shù)變成了jobject掌栅,這個(gè)jobj就是在java中調(diào)用非靜態(tài)native方法的那個(gè)對(duì)象,比如:
JniTest jt = new JniTest();
jt.getNameFromC();
jobj就表示對(duì)象jt熄求。而靜態(tài)的jclass就表示native方法聲明的那個(gè)類類型比如JniTest.class渣玲。
C層向Java層通信
- C層訪問(wèn)和修改Java層的屬性
我們?cè)趈ava中定義一個(gè)屬性name:
public String name = "Jerry";
為了方便編寫(xiě)使用C層來(lái)調(diào)用Java層的內(nèi)容,接下來(lái)都會(huì)先在Java層創(chuàng)建一個(gè)native方法弟晚,然后在C層native函數(shù)實(shí)現(xiàn)里來(lái)調(diào)用訪問(wèn)Java層的內(nèi)容忘衍。
/**
* @return 修改后的屬性內(nèi)容
*/
public native String accessField();
C中的實(shí)現(xiàn):
// 1. 訪問(wèn)java非屬性
// 修改java中屬性name的值并返回
JNIEXPORT jstring JNICALL Java_com_jerry_jnitest_JniTest_accessField
(JNIEnv *env, jobject jobj) {
// 獲取java中name屬性所在對(duì)象的class類類型
jclass jcls = (*env)->GetObjectClass(env, jobj);
// 獲取name屬性的fieldID
// (參數(shù):name表示Java中屬性的名字逾苫,最后一個(gè)參數(shù)表示屬性的類型簽名)
jfieldID fid = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;");
if (fid == NULL) {
printf("fid is NULL!");
}
// 通過(guò)屬性的fieldId獲取屬性的值
jstring jstr_fvalue = (*env)->GetObjectField(env, jobj, fid);
// 要想操作修改屬性值,得先把jstring -> 轉(zhuǎn)換成 c的字符串
// 最后一個(gè)參數(shù):JNI_TRUE是一個(gè)宏定義值是1枚钓,表示true需要拷貝(拷貝過(guò)一份內(nèi)存地址)铅搓,
// JNI_FALSE表示不需要拷貝(和java使用同一份字符串內(nèi)存地址),官方建議使用NULL
char *ch_fvalue = (*env)->GetStringUTFChars(env, jstr_fvalue, NULL);
// 使用string.h的字符串操作庫(kù)修改屬性值
char text[20] = "hello ";
char *new_fvalue = strcat(text, ch_fvalue);
// c -> java
jstring new_jstr = (*env)->NewStringUTF(env, new_fvalue);
// 修改屬性的值
(*env)->SetObjectField(env, jobj, fid, new_jstr);
return new_jstr;
}
代碼中注釋寫(xiě)的很清楚了搀捷,這里還需要注意的是關(guān)于屬性和方法的簽名規(guī)則:
數(shù)據(jù)類型 | 簽名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
Object | L開(kāi)頭星掰,然后以/分隔包的完整類型名,后面再加";"(分號(hào))嫩舟,比如說(shuō) String 簽名就是Ljava/lang/String; |
int[] Object[] |
以[開(kāi)頭氢烘,在加上數(shù)組元素類型的簽名,比如int[] 簽名就是[I , 再比如 int[][] 簽名就是 [[I ,object數(shù)組簽名就是 [Ljava/lang/Object; |
- C層訪問(wèn)和修改Java層的靜態(tài)屬性
在java中定義一個(gè)靜態(tài)屬性size:
private static int size = 26;
在C中實(shí)現(xiàn)一個(gè)函數(shù)用來(lái)修改java的這個(gè)靜態(tài)屬性:
// 2. 訪問(wèn)java靜態(tài)屬性
// 修改java中靜態(tài)屬性size的值并返回
JNIEXPORT void JNICALL Java_com_jerry_jnitest_JniTest_accessStaticField
(JNIEnv *env, jobject jobj) {
// 獲取jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
// 獲取jfieldID
jfieldID fID = (*env)->GetStaticFieldID(env, cls, "size", "I");
// 獲取屬性值
jint size= (*env)->GetStaticIntField(env, cls, fID);
size += 12;
// 修改屬性值
(*env)->SetStaticIntField(env, cls, fID, size);
}
經(jīng)過(guò)上面非靜態(tài)屬性的訪問(wèn)家厌,靜態(tài)的屬性就很簡(jiǎn)單了播玖,一樣的套路:獲取jclass -> 獲取屬性的jfieldID -> 獲取屬性值 ->設(shè)置屬性值。還有一個(gè)套路:獲取屬性值 一般都是GetStatic<Type>Field饭于,設(shè)置屬性值一般都是SetStatic<Type>Field蜀踏。
JNI系列文章:
JNI系列之入門(mén)Hello JNI C(一)
JNI系列之入門(mén)Hello JNI C(二)
JNI系列入門(mén)之C語(yǔ)言與Java的雙向通信(一)
JNI系列入門(mén)之C語(yǔ)言與Java的雙向通信(二)
JNI系列入門(mén)之C語(yǔ)言中文字符串亂碼問(wèn)題