1. Java Native Interface

命題作文:對Java Native Interface (JNI) 有一個認識,使用JNI完成一個打印輸出的工作廉涕。
本文通過回答三個環(huán)環(huán)相扣的問題宜雀,來對JNI的功能,作用做一個說明扳躬。

  1. JNI是干什么的?
  2. 面對一個問題甚亭,比如打印輸出贷币,JNI的工作流程是什么,每個步驟的目的是什么亏狰?
  3. 將每個步驟對應的代碼寫出來役纹,跑出來。

一暇唾、JNI是干什么用的

JNI的中文為JAVA本地調(diào)用促脉,本機上用其他語言編寫并且編譯過的代碼(在linux上就是so,在windows上就是dll)策州,可以通過這個本地接口(native interface)在java虛擬機(JVM)上運行[1]瘸味。

JNI一開始是為了本地已編譯語言,尤其是C和C++而設計的够挂,但是它并不妨礙你使用其他語言旁仿,只要調(diào)用約定受支持就可以了。JNI是JDK 的一部分孽糖,用于為Java提供一個本地代碼的接口枯冈。比如:Javah,產(chǎn)生可以調(diào)用Java過程的C過程梭姓,或建立能被Java程序調(diào)用的C過程的頭文件霜幼。

JNI使得運行在JVM虛擬機上的Java代碼能夠操作使用其它語言編寫的應用程序和庫,比如 C/C++以及匯編語言等誉尖。此外JNI提供的某些API還允許你把JVM嵌入到本地應用程序中。

JNI標準至少保證本地代碼能工作在任何Java 虛擬機下實現(xiàn)铸题。

使用JNI來整合本地代碼和Java代碼的步驟是確定的铡恕,沒有再創(chuàng)作的余地,所以讀者可以通過本文的步驟來逐步認識到丢间,其實Java也是"沒有什么不可以 "的探熔。

二、JNI的工作機制是什么

如果現(xiàn)有一個java類需要通過JNI來運行用c編寫的功能烘挫,比如打印輸出hello world诀艰。那么應該進行如下步驟:

  1. 編寫一個java類柬甥,在這個java類中,使用JNI加載需要調(diào)用的庫函數(shù)和這個庫函數(shù)中的具體方法其垄。
  2. 編譯這個java類苛蒲。然后運行這個類即可。前提是本地庫函數(shù)要準備好绿满。但是一般而言臂外,這個本地庫函數(shù)是沒有準備好的,或者說是要現(xiàn)場準備的喇颁。所以進入第三步漏健。
  3. 現(xiàn)在介紹怎么準備java類所要調(diào)用的用c語言編寫編譯過的庫函數(shù)。
  4. 為這個用c編寫的庫函數(shù)準備一個頭文件橘霎,這樣可以這個讓c編寫的庫函數(shù)與調(diào)用這個庫函數(shù)的java class進行交互蹂楣。頭函數(shù)是早期編程語言葱蝗,包括C語言特有的,用于函數(shù)的聲明,意思就是說有這么一個函數(shù)在衡载,名字叫啥啥啥。所以沃粗,在這里捕仔,這個頭函數(shù)里其實就是放了庫函數(shù)里的方法的聲明。
  5. 開始用C語言編寫這個庫函數(shù)橡卤。首先是寫成源代碼扮念。在這個源代碼中要調(diào)用為它準備好的頭文件。然后編寫這個庫函數(shù)的主要功能碧库,就是打印輸出hello world柜与,寫好后,編譯成庫函數(shù)嵌灰。一旦這個庫函數(shù)準備好后弄匕,就可以回到第二步,運行java 類文件沽瞭,然后就可以打印輸出了迁匠。

三、以上例子的代碼實現(xiàn)

  1. 編寫這個java 類
public class HelloJNI 
{ 
    static { System.loadLibrary("hello"); // Load native library at runtime   
                                          // hello.dll (Windows) or libhello.so (Unixes) 
    } 
    // Declare a native method sayHello() that receives nothing and returns void 
    private native void sayHello(); 
    // Test Driver 
    public static void main(String[] args) { new HelloJNI().sayHello(); // invoke the native method 
    }
}
  1. 將這個java 類編譯成class文件驹溃,命令如下:
    javac HelloJNI.java

    如果庫函數(shù)已經(jīng)準備好了城丧,就可以運行這個class文件,得到打印輸出的 Hello, World豌鹤!命令如下:
    java HelloJNI
    否則亡哄,進入第3步:

  2. 與第二節(jié)的步驟對應起來

  3. 把步驟2得到的class進一步處理,得到庫函數(shù)所需要的頭函數(shù)布疙。命令如下:
    javah HelloJNI

  4. 用C語言編寫庫函數(shù)蚊惯,代碼如下:

#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h" 
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) { 
        printf("Hello World!\n"); 
        return;
}

2.1. 將這個編寫好的源程序編譯成o文件愿卸,在linux下的代碼是:
gcc -fpIC -I/usr/lib/jvm/java-8-oracle/include -I/usr/lib/jvm/java-8-oracle/include/linux -c HelloJNI.c
其中,/usr/lib/jvm/java-8-oracle/include表示頭文件jni.h的所在路徑截型,/usr/lib/jvm/java-8-oracle/include/linux表示頭文件jni_md.h所在的路徑趴荸,/usr/lib/jvm/java-8-oracle表示JDK的安裝目錄。
2.2. 將這個o文件進一步連接成庫函數(shù)(so文件)菠劝,在linux下的代碼是:
gcc -o libhello.so -shared HelloJNI.o
2.3. 將so文件所在的路徑加入環(huán)境變量中去赊舶,在linux下的代碼是:
export LD_LIBRARY_PATH=/home/cxy/test
然后就可以回到第二步,用java HelloJNI命令運行class文件即可赶诊。
結(jié)果如下圖所示:

Paste_Image.png

本文結(jié)束

Second Try

step1. 填寫一個調(diào)用C語言的Java類

public class HelloJNI {
   static {
      System.loadLibrary("hello"); // Load native library at runtime
                                   // hello.dll (Windows) or libhello.so (Unixes)
   }
 
   // Declare a native method sayHello() that receives nothing and returns void
   private native void sayHello();
 
   // Test Driver
   public static void main(String[] args) {
      new HelloJNI().sayHello();  // invoke the native method
   }
}
  • static initializer invokes System.loadLibrary() to load a library.
  • the name of the library is hello/Hello. it is a native library.
  • the native library "Hello" contains a native method "sayHello()"
  • The native librry "Hello" will be mapped to libhello.so in Linux
  • This library should be included in Java's library path, otherwise, the program will throw a UnsatisfiedLinkError. You could include the library into Java Library's path export LD_LIBRARY_PATH=/home/x/Desktop/JNI
  • Next, we declare method "sayHello()" as a native instance method. native means this method is implemented in another language. The sayHello() is contained in the native library loaded.
  • The main() method allocate an instance of HelloJNI and invoke the native method sayHello(). new HelloJNI class's instance笼平,and invoke the native method sayHello() in it.
  • Question? why declare method sayHello as a native instance method? I mean maybe System.loadLibrary("hello"); does not know it is a native library? or native library constain method that is not native?

Compile the "HelloJNI.java" into "HelloJNI.class".
javac HelloJNI.java

  • javac 是java語言編程編譯器。全稱 javacompilation.javac工具讀由java語言編寫的類和接口的定義舔痪,并將它們編譯成字節(jié)代碼class文件寓调。My understanding is exe file.

Step2: Create the C/C++ Header file - HelloJNI.h 創(chuàng)建一個C的頭文件,命名為HelloJNI.h

Run javah utility on the class file to create a header file for C/C++ programs: javah HelloJNI

  • the utility means utility program that is "HelloJNI.class"
  • what does headfile use for? The declaration, tell the program the name of the fun. Then, according to the name of the fun, it can find the body.
  • give the C file with headfile "HelloJNI.h", it can be inferred that the c file invokes the function declared in "HelloJNI.h". Thus, to see what does the c file invokes, open it.
  • #include <jni.h> it include a jni.h headfile,
  • JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject); is a function written by c.
  • Java_{package_and_classname}_{function_name}(JNI arguments) the naming convention for C function 命名規(guī)則锄码。
  • Arguments is 命令行參數(shù)夺英。
    • JNIEnv*: reference to JNI environment, which lets you access all the JNI fucntions.
    • jobject: reference to "this" Java object.
  • The extern "C" is recognized by C++ compiler only. It notifies the C++ compiler that these functions are to be compiled using C's function naming protocol (instead of C++ naming protocol). C and C++ have different function naming protocols as C++ support function overloading and uses a name mangling scheme to differentiate the overloaded functions. Read "Name Mangling".

Step 3: C Implementation - HelloJNI.c

#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"
 
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
   printf("Hello World!\n");
   return;
}
  • HelloJNI.c 相當于一個子函數(shù),最終目的還是被調(diào)用滋捶,不同的是是被Java寫的程序調(diào)用痛悯,不是被C寫的程序調(diào)用。既然要被調(diào)用重窟,就要把HelloJNI.c里面的函數(shù)的函數(shù)名提取出來载萌,放在頭文件里。這個函數(shù)名就是Java_HelloJNI_sayHello巡扇。所以扭仁,JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject);是出現(xiàn)在HelloJNI.h這個頭文件里面的。
  • void is the data type厅翔,表示返回值是空值乖坠。
  • JNIEXPORT is a key word, JNIEXPORT :在Jni編程中所有本地語言實現(xiàn)Jni接口的方法前面都有一個"JNIEXPORT",這個可以看做是Jni的一個標志,至今為止沒發(fā)現(xiàn)它有什么特殊的用處刀闷。與被C語言調(diào)用做一個區(qū)分標識熊泵。
  • JNICALL :這個可以理解為Jni 和Call兩個部分,和起來的意思就是 Jni調(diào)用XXX(后面的XXX就是JAVA的方法名涩赢,就是HelloJNI這個類中的方法sayHello())戈次。
  • Java_HelloJNI_sayHello 就是被JNICALL調(diào)用的部分。也就是Java中的native 方法名筒扒,這里起名字的方式比較特別,是:包名+類名+方法名绊寻。

將這段C寫的子函數(shù)花墩,編譯成動態(tài)庫文件悬秉,這樣就可以被調(diào)用。
step1. gcc -fpic -I/usr/lib/jvm/java-8-oracle/include -I/usr/lib/jvm/java-8-oracle/include/linux -c HelloJNI.c其中冰蘑,/usr/lib/jvm/java-8-oracle/include表示頭文件jni.h的所在路徑和泌,/usr/lib/jvm/java-8-oracle/include/linux表示頭文件jni_md.h所在的路徑,/usr/lib/jvm/java-8-oracle表示JDK的安裝目錄祠肥。
step2. 將這個o文件進一步連接成庫函數(shù)(so文件)武氓,在linux下的代碼是:gcc -o libhello.so -shared HelloJNI.o
step3. 將so文件所在的路徑加入環(huán)境變量中去,在linux下的代碼是:export LD_LIBRARY_PATH=/home/cxy/test(注意:路徑不一樣)

step 4:執(zhí)行

然后就可以回到第二步仇箱,用java HelloJNI命令運行class文件即可县恕。
java HelloJNI
執(zhí)行java class文件,從主函數(shù)進去剂桥,new一個HelloJNI類忠烛,并調(diào)用類中的sayHello方法。這個sayHello并沒有像一般的java程序权逗,里面定義了執(zhí)行的代碼語句美尸,沒有輸入也沒有輸出。既然這樣是不是就斷了斟薇?不是师坎,這個sayHello給出了關鍵字native,說明是用其他語言執(zhí)行的堪滨。并且這個sayHello的執(zhí)行部分已經(jīng)寫在了"hello"這個函數(shù)庫里面胯陋。這個函數(shù)庫:hello:就對應了linux中的動態(tài)庫函數(shù)“l(fā)ibhello.so”。那么再調(diào)用這個“l(fā)ibhello.so”就可以繼續(xù)執(zhí)行下去椿猎。執(zhí)行這個動態(tài)庫函數(shù)首先去載入函數(shù)聲明惶岭。發(fā)現(xiàn)函數(shù)聲明JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject); 這個函數(shù)聲明正好對應了java class “HelloJNI” 中的方法 “sayHello”,也就是說java class中要調(diào)用的“sayHello”在這個動態(tài)庫so中找到了犯眠,那就順著這個聲明去找函數(shù)body按灶,要給出路徑才能找到,找到body就執(zhí)行這個body筐咧,返回空值鸯旁,java class “HelloJNI” 中的方法 “sayHello”獲得空值,執(zhí)行完畢量蕊。

FlowChat

編譯順序

Reference

  1. Jni接口-深入研究參數(shù)的傳遞(一)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铺罢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子残炮,更是在濱河造成了極大的恐慌韭赘,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,332評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件势就,死亡現(xiàn)場離奇詭異泉瞻,居然都是意外死亡脉漏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,508評論 3 385
  • 文/潘曉璐 我一進店門袖牙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侧巨,“玉大人,你說我怎么就攤上這事鞭达∷境溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,812評論 0 348
  • 文/不壞的土叔 我叫張陵畴蹭,是天一觀的道長坦仍。 經(jīng)常有香客問我,道長撮胧,這世上最難降的妖魔是什么桨踪? 我笑而不...
    開封第一講書人閱讀 56,607評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮芹啥,結(jié)果婚禮上锻离,老公的妹妹穿的比我還像新娘。我一直安慰自己墓怀,他們只是感情好汽纠,可當我...
    茶點故事閱讀 65,728評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著傀履,像睡著了一般虱朵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钓账,一...
    開封第一講書人閱讀 49,919評論 1 290
  • 那天碴犬,我揣著相機與錄音,去河邊找鬼梆暮。 笑死服协,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的啦粹。 我是一名探鬼主播偿荷,決...
    沈念sama閱讀 39,071評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼唠椭!你這毒婦竟也來了跳纳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,802評論 0 268
  • 序言:老撾萬榮一對情侶失蹤贪嫂,失蹤者是張志新(化名)和其女友劉穎寺庄,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,256評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡铣揉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,576評論 2 327
  • 正文 我和宋清朗相戀三年饶深,在試婚紗的時候發(fā)現(xiàn)自己被綠了餐曹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逛拱。...
    茶點故事閱讀 38,712評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖台猴,靈堂內(nèi)的尸體忽然破棺而出朽合,到底是詐尸還是另有隱情,我是刑警寧澤饱狂,帶...
    沈念sama閱讀 34,389評論 4 332
  • 正文 年R本政府宣布曹步,位于F島的核電站,受9級特大地震影響休讳,放射性物質(zhì)發(fā)生泄漏讲婚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,032評論 3 316
  • 文/蒙蒙 一俊柔、第九天 我趴在偏房一處隱蔽的房頂上張望筹麸。 院中可真熱鬧,春花似錦雏婶、人聲如沸物赶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酵紫。三九已至,卻和暖如春错维,著一層夾襖步出監(jiān)牢的瞬間奖地,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,026評論 1 266
  • 我被黑心中介騙來泰國打工赋焕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留参歹,地道東北人。 一個月前我還...
    沈念sama閱讀 46,473評論 2 360
  • 正文 我出身青樓宏邮,卻偏偏與公主長得像泽示,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蜜氨,可洞房花燭夜當晚...
    茶點故事閱讀 43,606評論 2 350

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,832評論 25 707
  • _ 聲明: 對原文格式以及內(nèi)容做了細微的修改和美化, 主要為了方便閱讀和理解 _ 一. 基礎 Java Nativ...
    元亨利貞o閱讀 5,900評論 0 34
  • 一械筛、NDK產(chǎn)生的背景 Android平臺從誕生起,就已經(jīng)支持C飒炎、C++開發(fā)埋哟。眾所周知,Android的SDK基于J...
    Ten_Minutes閱讀 3,487評論 1 27
  • 人群,潮水一般漲落 生生死死赤赊,街道依舊喧囂 于潦草的生年 逢著一種不期而遇 輾轉(zhuǎn)闯狱,分離 不問世事的歸期 于溫柔的波...
    慕籽閱讀 355評論 2 4
  • 2017年9月16日 周一 晴天 今天一整天都在忙碌的搬家搬東西,因為是一個人所以開會跑了三趟抛计,才把東西搬的差不多...
    娟姐66閱讀 108評論 0 0