JNI 入門(一):從Hello World開始

前言

最近在學習JNI的相關知識些阅,即Java Native Interface,它提供了若干API使得Java和C/C++的通信成為可能。我們知道,Java代碼運行于Java虛擬機中宇姚,獨立于某個平臺,這也是Java的可移植性的優(yōu)點夫凸。而C/C++代碼運行于Windows或Linux平臺浑劳。為了實現(xiàn)Java和其他代碼的交互,JNI應運而生夭拌。最簡單的就是魔熏,就是你在java中聲明一個方法,但方法的具體實現(xiàn)是由C/C++代碼來實現(xiàn)的鸽扁,然后生成dll庫或者so庫蒜绽,java層通過引入這個庫而調(diào)用這個Native方法,也就是我們在Android中會遇到的Native方法桶现。

JNI的優(yōu)點
1滓窍、JNI能調(diào)用本地操作系統(tǒng)所提供的本地方法,調(diào)用系統(tǒng)級的接口巩那。(dll庫是windows平臺吏夯,而so庫是Linux平臺)
2、對于一些以前用C/C++實現(xiàn)過的庫即横,可以用JNI來進行調(diào)用噪生,而不用重新在Java層實現(xiàn)一次,節(jié)省時間东囚。
3跺嗽、對于某些特殊場景,需要高效率地執(zhí)行代碼页藻,比如圖形渲染的過程桨嫁,這時候顯然使用C/C++能極大提高運行速度。

JNI的缺點
使用了JNI與本地方法交互之后份帐,犧牲了Java的可移植性這一優(yōu)點璃吧,因為windwos和linux是不同的形式的庫,必須在具體平臺下重新編譯废境。

筆者的環(huán)境平臺
在開始之前畜挨,先說一下筆者所使用的IDE以及平臺
1筒繁、Windows 10操作系統(tǒng)
2、IntelliJ IDEA Community Edition 2018.3.5 x64
3巴元、Visual Studio 2017
4毡咏、JDK10

從Hello World開始

好了,說了一大堆逮刨,那么就讓我們開始學習JNI呕缭,先從最簡單的Hello World開始,先讓代碼跑起來修己,然后再繼續(xù)深化學習臊旭。

1、在Intellij新建一個java項目

Java項目結(jié)構(gòu)

①NativeTest類箩退,里面僅聲明了一個Native方法:


NativeTest類.png

native關鍵字表示這個方法由native層來實現(xiàn)离熏,我們在java層不需要關注它的實現(xiàn)。

②Test類戴涝,這里聲明了main方法滋戳,并調(diào)用NativeTest的native方法。


Test類.png

2啥刻、為native方法所在的類生成相應的頭文件
上面的native方法在NativeTest類中奸鸯,我們需要為此生成一個.h文件,只有這樣我們才能把.h文件的方法用C/C++代碼去實現(xiàn)可帽。那么娄涩,怎么生成一個.h文件呢?JDK為我們提供了工具專門用于生成JNI所用的頭文件映跟。
格式是:javac -h <directory> <source file>
<directory>表示生成的.h文件所放置的位置蓄拣。
<source file>表示待編譯的源文件。
上面的命令需要在命令行中使用努隙,我們可以用Intellij提供的Terminal去輸入球恤,也可以使用windows的cmd去輸入,二者的效果是一樣的荸镊。為了方便起見咽斧,筆者這里使用的是Terminal。

2.1躬存、首先张惹,我們在Terminal定位到NativeTest類所在的文件夾,即:


命令行1.png

2.2岭洲、然后宛逗,通過使用javac -h命令,編譯NativeTest類钦椭,并生成與這個類有關的.h文件:


命令行2.png

注意:這里的<directory>使用了"."拧额,表示在當前目錄直接生成.h文件。<source file>這里填入NativeTest.java彪腔,因為當前目錄下有NativeTest.java文件侥锦,所以不需要添加額外的路徑了。
2.3德挣、運行之后恭垦,會發(fā)現(xiàn)當前目錄下多了兩個文件:NativeTest.class和com_jni_NativeTest.h文件:
項目目錄結(jié)構(gòu)2.png

所以我們知道,javac -h實際上做了兩部操作格嗅,對NativeTest.java進行編譯生成class文件番挺,然后再生成.h文件。

3屯掖、新建一個windows桌面程序項目
這一步玄柏,在VS 2017中進行,新建一個項目如下圖所示:

VS項目創(chuàng)建.png

我們這里選擇動態(tài)鏈接庫(DLL)贴铜,因為我們需要這個dll庫供java層使用粪摘,這里項目的位置可以保存在任何一個地方。

3.1绍坝、項目創(chuàng)建完畢之后徘意,我們把剛才生成的com_jni_NativeTest.h文件復制到當前項目的文件目錄下,如下圖:


復制文件的路徑.png

然后在VS2017的“解決方法資源管理器”中轩褐,在“頭文件選項”椎咧,右鍵選擇添加已有項,選中當前目錄下剛才復制進來的com_jni_NativeTest.h文件:


添加現(xiàn)有項.png

3.2把介、接下來勤讽,我們需要把一些額外的文件,打開jdk的安裝目錄(筆者jdk的安裝目錄為:C:\Program Files\Java\jdk-10)拗踢,在include文件夾下地技,復制jni.h和include\win32內(nèi)的jni_md.h 這兩個文件到NativeCode項目,即剛才存放com_jni_NativeTest.h文件的目錄秒拔,同時把這兩個文件作為現(xiàn)有項添加到頭文件莫矗,步驟3.1的最后。
完成3.1和3.2之后砂缩,我們會看到有如下的頭文件結(jié)構(gòu):


頭文件目錄結(jié)構(gòu).png

3.3作谚、打開項目中的com_jni_NativeTest.h文件,把頂部的#include<jni.h>改成#include"jni.h"庵芭,這樣就不會報錯了妹懒。解釋一下為什么要做這樣的改動:#include<>形式表示C/C++文件編譯時,首先從編譯器的類庫路徑里面尋找該頭文件双吆;而#include""表示在當前文件目錄下尋找頭文件眨唬。

3.4会前、關于com_jni_NativeTest.h文件的補充說明。
我們打開這個頭文件匾竿,觀察其內(nèi)部結(jié)構(gòu):

頭文件內(nèi)部結(jié)構(gòu).png

我們可以看到瓦宜,該頭文件聲明了一個方法Java_com_jni_NativeTest_sayHello(JNIEnv,jobject)岭妖,該方法有兩個關鍵字分別為JNIEXPORT和JNICALL 表示這個方法是要從Java層被調(diào)用的临庇。然后該函數(shù)有兩個形參:JNIEnv** 和 jobject。這兩個參數(shù)是native方法自帶的參數(shù)昵慌。
JNIEnv *是一個函數(shù)指針假夺,它指向一系列JNI提供的函數(shù)來進行數(shù)據(jù)操作。
jobject表示調(diào)用這個函數(shù)的對象斋攀,因為在java層它是一個實例方法已卷,所以實際上這個參數(shù)的作用類似于 this關鍵字

4淳蔼、新建一個c++文件悼尾,實現(xiàn).h文件所聲明的函數(shù)
這里筆者直接使用VS幫我們生成的NativeCode.cpp文件進行操作:

C++文件實現(xiàn).png

這里僅簡單輸出了hello world。

5肖方、編譯生成DLL文件
這里要使用x64的Debug調(diào)試器(默認是x86)闺魏,點擊上方的生成——>生成解決方案,可以觀察到控制臺輸出了信息俯画,并標明了生成的dll文件所在的位置析桥,我們前往該位置,一般在 /項目目錄/x64/debug/NativeCode.dll艰垂。我們復制這個庫文件到Java項目內(nèi)泡仗。在這里,筆者在java項目跟目錄下猜憎,新建了一個native_libs的文件夾娩怎,把dll文件復制到這里:

Java項目結(jié)構(gòu)2.png

6、加載DLL文件胰柑,并運行Main函數(shù)
DLL文件已經(jīng)被添加到我們的Java項目了截亦,接下來的操作就是要在JVM運行時加載它,以便我們后續(xù)調(diào)用native方法柬讨。我們在Test.java文件作點修改:

Test類2.png

好了崩瓤,到目前為之,所有的工作都已經(jīng)做完踩官,讓我們運行一下Main函數(shù)却桶,看一下效果如何?


運行結(jié)果.png

如果你看到了上面的輸出,那么恭喜你颖系,你已經(jīng)掌握了JNI調(diào)用的基本方法嗅剖!

小結(jié)

上面詳述了實現(xiàn)一個簡單JNI調(diào)用的步驟,現(xiàn)在小結(jié)一下整個流程嘁扼。
(1)在一個Java類中聲明native方法信粮。
(2)利用javac -h命令以該類為源文件生成一個.h文件
(3)在C/C++文件中實現(xiàn)該頭文件所聲明的方法
(4)編譯C/C++文件,生成一個DLL或so文件偷拔,把它添加到java項目中
(5)通過System.loadLibrary方法加載這個庫文件

踩過的坑

下面談談筆者在學習過程中遇到的一些問題蒋院,避免各位讀者再度踩坑亏钩。
1莲绰、關于javac -h和javah命令
筆者在剛學習JNI的知識時,在生成.h文件階段姑丑,在網(wǎng)上查的教程都是利用javah命令來進行操作蛤签。然而在筆者的電腦總提示"javah不是內(nèi)部命令",然而javac是可以正常運作的栅哀,這說明并不是環(huán)境變量出了問題震肮,這就有可能是jdk安裝目錄下壓根就沒有javah.exe,經(jīng)過查找留拾,確實是沒有這個文件戳晌,所以javah命令會運行失敗。
那么問題來了痴柔,為什么我的JDK沒有javah.exe呢沦偎?
經(jīng)過查閱資料,原來在JDK10以上的JDK內(nèi)部已經(jīng)去除了javah命令咳蔚,它的功能被整合進了javac -h命令內(nèi)豪嚎。然而在jdk8以下的jdk是有該命令的。

解決途徑:如果電腦安裝的是jdk10以及10以上谈火,使用javac -h命令侈询;而jdk8以及8以下的使用javah -jni命令。

2糯耍、javac命令的進一步說明
javac命令是編譯命令扔字,將java源文件編譯成class字節(jié)碼文件。我們可以在命令行輸入javac -help温技,了解它的使用方法:

javac.png

其基本語法為:javac <options> <source files>啦租,其中<options>可以是多個,而且<source files>也可以是多個荒揣,這時候表示把多個源文件同時編譯篷角。所以生成.h文件的命令格式為:javac -h <directory> <source files>
需要注意的是:如果少了<directory>選項(如果是當前目錄直接用" . "代替),就會報錯系任,筆者曾在這里卡了很長時間恳蹲。

這篇文章到這里就結(jié)束了虐块,希望對各位同學有所裨益:) 謝謝看到這里的你。下一篇文章將會圍繞JNI的數(shù)據(jù)操作嘉蕾、函數(shù)操作部分進行詳細講解贺奠。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市错忱,隨后出現(xiàn)的幾起案子儡率,更是在濱河造成了極大的恐慌,老刑警劉巖以清,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件儿普,死亡現(xiàn)場離奇詭異,居然都是意外死亡掷倔,警方通過查閱死者的電腦和手機眉孩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勒葱,“玉大人浪汪,你說我怎么就攤上這事×菟洌” “怎么了死遭?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凯旋。 經(jīng)常有香客問我呀潭,道長,這世上最難降的妖魔是什么瓦阐? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任蜗侈,我火速辦了婚禮,結(jié)果婚禮上睡蟋,老公的妹妹穿的比我還像新娘踏幻。我一直安慰自己,他們只是感情好戳杀,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布该面。 她就那樣靜靜地躺著,像睡著了一般信卡。 火紅的嫁衣襯著肌膚如雪隔缀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天傍菇,我揣著相機與錄音猾瘸,去河邊找鬼。 笑死,一個胖子當著我的面吹牛牵触,可吹牛的內(nèi)容都是我干的淮悼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼揽思,長吁一口氣:“原來是場噩夢啊……” “哼袜腥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钉汗,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤羹令,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后损痰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體福侈,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年徐钠,在試婚紗的時候發(fā)現(xiàn)自己被綠了癌刽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片役首。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出烙如,到底是詐尸還是另有隱情吕喘,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布矮固,位于F島的核電站失息,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏档址。R本人自食惡果不足惜盹兢,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望守伸。 院中可真熱鬧绎秒,春花似錦、人聲如沸尼摹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蠢涝。三九已至玄呛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間和二,已是汗流浹背徘铝。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惕它。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓场晶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親怠缸。 傳聞我的和親對象是個殘疾皇子诗轻,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

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