JNI系列入門(mén)之C語(yǔ)言與Java的雙向通信(一)

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)題


一欢顷、概述

  1. Java層向C層通信
  • 通過(guò)調(diào)用靜態(tài)無(wú)參數(shù)懂酱、有參數(shù)的native方法
  • 通過(guò)調(diào)用非靜態(tài)無(wú)參數(shù)、有參數(shù)的native方法
  1. 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é)果:


Paste_Image.png

這是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)題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掰吕,隨后出現(xiàn)的幾起案子果覆,更是在濱河造成了極大的恐慌,老刑警劉巖殖熟,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件局待,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吗讶,警方通過(guò)查閱死者的電腦和手機(jī)燎猛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)照皆,“玉大人,你說(shuō)我怎么就攤上這事沸停∧せ伲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵愤钾,是天一觀的道長(zhǎng)瘟滨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)能颁,這世上最難降的妖魔是什么杂瘸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮伙菊,結(jié)果婚禮上败玉,老公的妹妹穿的比我還像新娘敌土。我一直安慰自己,他們只是感情好运翼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布返干。 她就那樣靜靜地躺著,像睡著了一般血淌。 火紅的嫁衣襯著肌膚如雪矩欠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天悠夯,我揣著相機(jī)與錄音癌淮,去河邊找鬼。 笑死沦补,一個(gè)胖子當(dāng)著我的面吹牛乳蓄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播策彤,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼栓袖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了店诗?” 一聲冷哼從身側(cè)響起裹刮,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎庞瘸,沒(méi)想到半個(gè)月后捧弃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡擦囊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年违霞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞬场。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡买鸽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贯被,到底是詐尸還是另有隱情眼五,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布彤灶,位于F島的核電站看幼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏幌陕。R本人自食惡果不足惜诵姜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搏熄。 院中可真熱鬧棚唆,春花似錦暇赤、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至摆寄,卻和暖如春失暴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背微饥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工逗扒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欠橘。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓矩肩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肃续。 傳聞我的和親對(duì)象是個(gè)殘疾皇子黍檩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容