前言:html
一提到JNI,多數編程者會下意識地感覺到一種沒法言喻的恐懼。它給人的第一感受就是"難",由於它不是單純地在JVM環境內操做Java代碼,而是跳出虛擬機與其餘編程語言進行交互。java
你可能至今還沒據說過這個技術,可是若是你是一個源碼愛好者,或者有翻閱過JDK的一些源碼,那你必定有接觸過native方法。你是否由於查閱源碼直到native方法戛然而止,但又因爲它的空方法體,而對底層原理不知因此? 本文就帶讓你瞭解JNI。並經過一些案例來本身實現JNI的交互。編程
什麼是JNI?windows
JNI 全稱 Java Native Interface。Java本地方法接口,它是Java語言容許Java代碼與C、C++代碼交互的標準機制。維基百科是這樣解釋的:「當應用沒法徹底用Java編程語言實現的時候,(例如,標準Java類庫不支持的特定平臺特性或者程序庫時),JNI使得編程者可以編寫native方法來處理這種狀況」。這就意味着,在一個Java應用程序中,咱們可使用咱們須要的C++類庫,而且直接與Java代碼交互,並且在能夠被調用的C++程序內,反過來調用Java方法(回調函數)。編程語言
JNI的優勢:函數
(1)JNI使得一些"過程"無需在Java中實現。例如,硬件敏感的,或者直接與操做系統API關聯的命令。工具
(2)因爲使用底層的庫,如圖形,計算,各類類型的渲染等等,能夠提升應用的運行性能。性能
(3)已經有大量的庫已經被實現,編程者可直接使用,不用再自行編寫。這裏的庫指的是用其餘編程語言實現的程序庫,例如IO流或者線程等底層與OS交互的操做都是由C/C++實現的。spa
具體實現原理操作系統
交互模式如圖
要從Java調用C++函數,你須要進行如下操做:
1. 在Java類中建立一個native方法,此方法被本類其餘方法調用
2. 建立一個頭文件,能夠利用javah命令生成。
在頭文件中定義它的簽名,以下所示:
接口規範:
JNIEXPORT <返回類型> JNICALL Java_<包名>_<類名>_<方法名>(JNIEnv*, <原對象引用>,<參數1>..<參數n>)
3. 建立一個源文件,實現頭文件中定義的接口。實現內容就是Java代碼調用的C/C++代碼。
4. 編譯頭文件和源文件生成C/C++動態連接庫 .so/.dll 文件
5. 此native方法所在類,加載動態連接庫。由於加載連接庫要在執行native方法以前,因此此加載過程通常放在靜態初始化塊內執行。
或
總結一下,從Java代碼中調用C/C++代碼的流程:
(1)建立一個有native標識的方法,而且從其餘Java方法調用它
(2)Java編譯器生成字節碼
(3)C/C++ 編譯器生成動態庫 .so文件(Linux)或 .dll文件(Windows)
(4)運行程序,執行字節碼
(5)執行到loadLibary或load調用的時候,添加一個 .so文件到這個進程中
(6)執行到native方法的時候,經過方法簽名,在已打開的.so文件中進行搜索。
(7)若是連接庫內有對應方法,就會被執行,不然程序崩潰
注:因爲windows沒找到生成動態連接庫的工具,又不想安裝C/C++開發環境,故如下案例都在以CentOS爲操做系統的虛擬機內運行
案例一:從Java調用C代碼輸出Hello World
此案例全部生成的全部文件以下:
(1)建立JNI文件夾,建立Java文件以下:
這裏,咱們定義了一個native方法,是個空方法體,咱們在主函數內對其進行調用。
注:這裏使用的是System.load從絕對路徑引用動態連接庫,固然也可使用loadLibrary方法,其是從java.library.path對應路徑下搜索對應名稱的庫文件並加載。
(2)編譯HelloJNI.java文件,生成類文件
(3)利用JDK提供的JNI命令工具,javah生成 .h頭文件。
注:發現Linux環境下,javah竟然不能從當前文件夾掃描到類文件,須要指定類路徑 其中 -cp 就是-classpath
如下是利用javah生成的頭文件。
(4)建立HelloJNI.c文件,編寫實現體
(5)利用gcc生成動態連接庫,注意咱們這裏有引用到jni.h這個頭文件,此文件由JDK提供,另外jni.h還引用了jni_md.h這個文件。必須引入這兩個頭文件,才能經過編譯。
兩個文件的所在地,本人JDK的安裝路徑在/usr/java下,每一個人可能都不同。
在gcc命令內經過指定( -I 路徑 )引入庫所在的目錄,利用前面前面的頭文件和源文件編譯成動態連接庫 hello.so
(6)運行java程序
由圖可知,咱們成功調用了C的代碼