歡迎點擊「算法與編程之美」↑關注我們!java
本文首發於微信公衆號:"算法與編程之美",歡迎關注。c++
互聯網上已經有不少介紹 JNI 的入門教程,爲何還要畫蛇添足寫本文呢?算法
相信你們在平時閱讀一些教程類文章時都遇到過這樣的狀況,按照教程描述的步驟一步步的來操做,結果卻並無獲得教程指望的結果。遇到各類各樣的問題,最後解決不了這些問題,進而放棄,放棄的緣由很簡單那就是對於一個未知的領域,讀者遇到問題後沒法本身解決,而所閱讀的教程類文章並無對這一問題進行詳細的描述,致使最後選擇了放棄。編程
經過分析咱們發現,大多的教程類文章都有一個共同的問題,就是重步驟而忽視讀者在閱讀過程當中可能遇到的問題的分析。windows
咱們指望改變這一現狀,對於教程類文章,咱們不只介紹具體的操做步驟,並且更重要的是介紹讀者在進行操做時可能遇到的一些關鍵問題,並對這些問題進行詳細分析,從而幫助您完全的解決問題。微信
下面將介紹編寫 JNI 入門教程HelloNative程序的編寫。編程語言
主要的步驟爲:函數
1) 編寫 HelloNative.java 程序;工具
2) 編譯並獲得 HelloNative.h 頭文件;學習
3) 編寫 HelloNative.c 程序;
4) 編譯動態連接庫libHelloNative.jnilib;
5) 運行HelloNative程序。
先從總體上了解一下咱們須要作的事情有哪些,接下來我將介紹在mac 系統下每個步驟的詳細內容並標註難點。
public class HelloNative{ static{ System.loadLibrary("HelloNative"); //難點一 } public static native void sayHello(); public static void main(String[]args){ new HelloNative().sayHello(); } }
執行以下命令:
javac HelloNative.java javah HelloNative
#include <stdio.h> #include "HelloNative.h" JNIEXPORT void JNICALL Java_HelloNative_sayHello(JNIEnv *env, jclass jc) //難點二 { printf("Hello Native\n"); }
//難點三
gcc HelloNative.c -o libHelloNative.jnilib -dynamiclib -I/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/ -I/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin/
java HelloNative
雖然上面介紹的是mac 系統下程序的編寫,讀者的系統可能大可能是windows,可是不影響你們的編寫。
接下來將對第2節中標記的三個難點進行分析,這三個難點也是你們遇到的最多的問題。
JNI存在的意義就在於可以讓 Java 程序和 C/C++等其餘編程語言之間可以很是方便的交互,經過JNI 咱們就可以很是方便的作到這一點。
有些同窗可能會問爲何要這樣作?全部的任務我都用 Java 來作不是更好嗎,爲何一會用 Java 一會用C/C++,這樣多語言豈不是更麻煩嗎。
這個問題其實有多方面的緣由,如下將列舉幾點緣由:
- Java語言是運行在 JVM 之上的,所以對JVM 依賴的很是高。衆所周知,這樣的機制使得 Java 語言相對其餘C 語言來講效率變得低下,所以一些對執行效率要求較高的任務咱們能夠用 C 語言來編寫,而後上面的程序能夠經過JNI 來調用 C 編寫的模塊。
- 假設你的項目組是一個多語言的組,存在着 Java、Ruby、Python 等多種編程語言的人員,如何讓這些人員編程的程序可以相關調用呢?
那麼 Java 是如何作到這一點的呢?
將其餘語言編寫的模塊編譯成動態庫,而後在Java程序中加載這個動態庫,進而使用該庫。
System.loadLibrary("HelloNative"); //難點一
所以你們看到的這行代碼就是 Java 程序加載編譯後的動態庫HelloNative。這裏面你們須要注意的是HelloNative 並非最終動態庫的全稱,不一樣的操做系統下這個動態庫的名稱是不同的,如:
Windows 下叫*.dll
Linux 下叫*.so
Mac 下叫*.jnilib
你們都知道 Java 是跨平臺的程序,所以在Java 代碼裏面確定不能指定某一種平臺具體的動態庫完整名稱,所以只是給出了這個動態庫的名稱而非全稱。
對於本例咱們最終在不一樣平臺下生成的動態庫全稱是:
Windows HelloNative.dll Linux libHelloNative.so Mac libHelloNative.jnilib
這個知識點很是有用,難點3中會再次涉及,請你們務必提早掌握。
這個函數的定義你們可能會寫錯,並且你們一般狀況下不能徹底按照教程來寫。
若是你們寫過 c/c++ 語言的程序應該都知道,在c/c++中,一個程序分爲頭文件 hello.h 和其實現文件hello.c,其中在頭文件中定義了函數聲明,而在實現文件中對函數進行實現。
在瞭解這個知識點之後,咱們就知道 HelloNative.c 文件中應該如何突破難點2了。
打開 HelloNative.h 文件,找到對應的函數聲明。
JNIEXPORT void JNICALL Java_HelloNative_sayHello (JNIEnv *, jclass);
函數聲明你們應該都能看懂,本例中你們須要注意的是,在形參的聲明中能夠不指定形參的名稱,而只是給出形參的類型。所以本例中的JNIEnv* 和jclass都是形參的類型。
因此難點二中你們看到的代碼是下面這樣,其中env和jc是具體的形參名稱。
JNIEXPORTvoid JNICALL Java_HelloNative_sayHello(JNIEnv *env, jclass jc) //難點二
這個難點也是你們遇到的最多問題的地方,須要重點闡述。
如何編譯一個C 語言的動態庫?
你們都知道 C 語言一個很是著名的編譯器叫gcc,所以本文介紹如何用 gcc 來編譯動態庫。(這裏面你們須要注意的是 gcc 只是c語言編譯器其中的一種,除了 gcc 還有不少其餘的編譯器如微軟等公司出品的。)
不一樣的操做系統下編譯動態庫的命令也是不同的,如:
Windows下: gcc -shared HelloNative.c -o HelloNative.dll Linux 下: gcc -shared HelloNative.c -o libHelloNative.so Mac 下: gcc -dynamiclib HelloNative.c -o libHelloNative.jnilib
在難點一中咱們給你們闡述了不一樣的系統中動態庫的名稱是不同的,本節就有所體現。
其次,咱們還須要給出gcc編譯動態庫中jni.h和jni_md.h兩個頭文件的路徑。
所以,咱們接下來要找到這兩個頭文件所在的目錄,你能夠經過各類各樣的文件搜索工具(如 windows 下的強大的搜索神器everything)找到他們。在 Linux 和Mac 下面咱們能夠經過下面的命令快速找到:
locate jni.h >/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/jni.h locate jni_md.h >/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin/jni_md.h
注意,咱們只須要兩個文件所在的目錄:
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin/
準備工做作好了,最後咱們加上這個頭文件的路徑就大功告成了。
Mac 下:
gcc -dynamiclib HelloNative.c -o libHelloNative.jnilib -I/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/ -I/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin/
注意:
此處咱們只給出 mac 下的路徑,其餘系統相似。
本文介紹了 JNI 的入門教程HelloNative程序的編寫,相對於其餘的教程,咱們更加劇視你們在實際學習過程當中的一些難點,經過對三個難點的分析,相信你們可以更好的掌握相關的知識點。
如您對此種教程類文章感興趣,歡迎持續關注「算法與編程之美」微信公衆號,瞭解更多此類文章。