Java 本地接口 JNI 使用詳解

轉(zhuǎn)文地址:http://www.codeceo.com/article/java-jni-usage.html

?對(duì)于java程序員來(lái)說(shuō),java語(yǔ)言的好處和優(yōu)點(diǎn)鸡捐,我想不用我說(shuō)了,大家自然會(huì)說(shuō)出很多一套套的。但雖然我們作為java程序員,但我們不得不承認(rèn)java語(yǔ)言也有一些它本身的缺點(diǎn)嗡呼。比如在性能、和底層打交道方面都有它的缺點(diǎn)皇耗。所以java就提供了一些本地接口南窗,他主要的作用就是提供一個(gè)標(biāo)準(zhǔn)的方式讓java程序通過(guò)虛擬機(jī)與原生代碼進(jìn)行交互,這也就是我們平常常說(shuō)的java本地接口(JNI——java native Interface)郎楼。它使得在 Java 虛擬機(jī)(VM) 內(nèi)部運(yùn)行的Java 代碼能夠與用其它編程語(yǔ)言(如 C矾瘾、C++ 和匯編語(yǔ)言)編寫(xiě)的應(yīng)用程序和庫(kù)進(jìn)行互操作。JNI 最重要的好處是它沒(méi)有對(duì)底層 Java 虛擬機(jī)的實(shí)現(xiàn)施加任何限制箭启。因此,Java虛擬機(jī)廠(chǎng)商可以在不影響虛擬機(jī)其它部分的情況下添加對(duì)JNI 的支持蛉迹。程序員只需編寫(xiě)一種版本的本地應(yīng)用程序或庫(kù)傅寡,就能夠與所有支持JNI 的Java 虛擬機(jī)協(xié)同工作。我們來(lái)看一下為什么要與原生代碼進(jìn)行交互:

一:提高應(yīng)用程序性能。我們知道java對(duì)于c/c++荐操、匯編語(yǔ)言來(lái)說(shuō)芜抒,顯得比較“高級(jí)”。其實(shí)這里的高級(jí)就是簡(jiǎn)化了程序員的工作托启。很多底層的東西都讓java虛擬機(jī)做了宅倒。但畢竟相對(duì)于直接訪(fǎng)問(wèn)底層來(lái)講,java多了一步虛擬機(jī)的過(guò)程屯耸,所以在性能上比著這些原生語(yǔ)言稍微有點(diǎn)慢拐迁。

二:實(shí)現(xiàn)一些與底層相關(guān)的功能。Java平臺(tái)提供的標(biāo)準(zhǔn)類(lèi)庫(kù)疗绣,還有強(qiáng)大的API线召,雖然能完成大部分功能。但有些和底層硬件打交道的功能在java?API提供的類(lèi)庫(kù)中還是無(wú)法完成多矮。

三:與已有的使用原生代碼編寫(xiě)的程序進(jìn)行集成缓淹。在于操作系統(tǒng)上由c或者c++等原生語(yǔ)言編寫(xiě)的軟件進(jìn)行集成的時(shí)候,可以用JNI塔逃。

JNI?接口函數(shù)和指針

平臺(tái)相關(guān)代碼是通過(guò)調(diào)用?JNI?函數(shù)來(lái)訪(fǎng)問(wèn)Java?虛擬機(jī)功能的讯壶。JNI?函數(shù)可通過(guò)接口指針來(lái)獲得。接口指針是指針的指針湾盗,它指向一個(gè)指針數(shù)組伏蚊,而指針數(shù)組中的每個(gè)元素又指向一個(gè)接口函數(shù)。每個(gè)接口函數(shù)都處在數(shù)組的某個(gè)預(yù)定偏移量中淹仑。下圖說(shuō)明了接口指針的組織結(jié)構(gòu)丙挽。

JNI?接口的組織類(lèi)似于C++?虛擬函數(shù)表或COM?接口。使用接口表而不使用硬性編入的函數(shù)表的好處是使JNI?名字空間與平臺(tái)相關(guān)代碼分開(kāi)匀借。虛擬機(jī)可以很容易地提供多個(gè)版本的JNI?函數(shù)表颜阐。例如,虛擬機(jī)可支持以下兩個(gè)JNI?函數(shù)表:

1)一個(gè)表對(duì)非法參數(shù)進(jìn)行全面檢查吓肋,適用于調(diào)試程序凳怨;

2)另一個(gè)表只進(jìn)行?JNI?規(guī)范所要求的最小程度的檢查,因此效率較高是鬼。

JNI?接口指針只在當(dāng)前線(xiàn)程中有效肤舞。因此,本地方法不能將接口指針從一個(gè)線(xiàn)程傳遞到另一個(gè)線(xiàn)程中均蜜。實(shí)現(xiàn)?JNI?的虛擬機(jī)可將本地線(xiàn)程的數(shù)據(jù)分配和儲(chǔ)存在?JNI?接口指針?biāo)赶虻膮^(qū)域中李剖。

本地方法將JNI?接口指針當(dāng)作參數(shù)來(lái)接受。虛擬機(jī)在從相同的?Java?線(xiàn)程中對(duì)本地方法進(jìn)行多次調(diào)用時(shí)囤耳,保證傳遞給該本地方法的接口指針是相同的篙顺。但是偶芍,一個(gè)本地方法可被不同的?Java?線(xiàn)程所調(diào)用,因此可以接受不同的?JNI?接口指針德玫。

1)編寫(xiě)Java類(lèi)代碼

其中匪蟀,需要JNI實(shí)現(xiàn)的方法應(yīng)當(dāng)用native關(guān)鍵字聲明,在該類(lèi)中,用System.loadLibrary()方法加載需要的動(dòng)態(tài)鏈接庫(kù),關(guān)鍵代碼如下:

//Compute.java

public?class?Compute{

public?native?double?sqrt(double??params);

static{

//調(diào)用動(dòng)態(tài)鏈接庫(kù)

System.loadLibrary("compute")宰僧;

}

}

2)編譯成字節(jié)代碼

在這個(gè)過(guò)程中材彪,由于采用了native關(guān)鍵字聲明,Java編譯器會(huì)忽視沒(méi)有代碼體的JNI方法部分琴儿。

3)生成相關(guān)JNI方法的頭文件

這個(gè)過(guò)程的實(shí)現(xiàn)一般是通過(guò)利用jlavah-jni??*?class生成的(-jni可以省略)段化,也可以手工生成該文件;但是由于?Java?虛擬機(jī)是根據(jù)一定的命名規(guī)范完成對(duì)JNI方法的調(diào)用凤类,所以手工編寫(xiě)頭文件需要特別小心穗泵。

上述文件產(chǎn)生的頭文件部分代碼如下:

//Compute.h

extern“C”{

JNIEXPORT?jdoubleJNICALL?Java_Compute_comp(JNI-Env?*,?jobject,?jdoubleArray);

JNI函數(shù)名稱(chēng)分為三部分:首先是Java關(guān)鍵字谜疤,供Java虛擬機(jī)識(shí)別佃延;然后是調(diào)用者類(lèi)名稱(chēng)(全限定的類(lèi)名,其中用下劃線(xiàn)代替名稱(chēng)分隔符)夷磕;最后是對(duì)應(yīng)的方法名稱(chēng)履肃,各段名稱(chēng)之間用下劃線(xiàn)分割。

JNI函數(shù)的參數(shù)也由三部分組成:首先是JNIEnv?*,是一個(gè)指向JNI運(yùn)行環(huán)境的指針坐桩;第二個(gè)參數(shù)隨本地方法是靜態(tài)還是非靜態(tài)而有所不同一一非靜態(tài)本地方法的第二個(gè)參數(shù)是對(duì)對(duì)象的引用尺棋,而靜態(tài)本地方法的第二個(gè)參數(shù)是對(duì)其Java類(lèi)的引用;其余的參數(shù)對(duì)應(yīng)通常Java方法的參數(shù)绵跷,參數(shù)類(lèi)型需要根據(jù)一定規(guī)則進(jìn)行映射膘螟。

4)編寫(xiě)相應(yīng)方法的實(shí)現(xiàn)代碼

在編碼過(guò)程中,需要注意變量的長(zhǎng)度問(wèn)題碾局,例如Java的整型變量長(zhǎng)度為32位荆残,而C語(yǔ)言為16位,所以要仔細(xì)核對(duì)變量類(lèi)型映射表,防止在傳值過(guò)程中出現(xiàn)問(wèn)題。

5)將JNI實(shí)現(xiàn)代碼編譯成動(dòng)態(tài)鏈接庫(kù)

編譯過(guò)程是利用C/C++編譯器實(shí)現(xiàn)的叨咖,在windows平臺(tái)上,編譯和連接的結(jié)果是動(dòng)態(tài)鏈接庫(kù)DLL文件俘闯。當(dāng)要使用生成的動(dòng)態(tài)鏈接庫(kù)時(shí),調(diào)用者類(lèi)中需要顯式調(diào)用該鏈接庫(kù)dll文件忽冻。

經(jīng)過(guò)上述處理真朗,基本上完成了一個(gè)包含本地化方法的Java類(lèi)的開(kāi)發(fā)。

附錄:將Jav類(lèi)型映射到本地C?類(lèi)型

為了使用方便僧诚,特提供以下定義遮婶。

#define?JNI_FALSE??0

#define?JNI_TRUE???1

jsize?整數(shù)類(lèi)型用于描述主要指數(shù)和大行懔狻:

typedef?jint?jsize;

故障排除

當(dāng)使用?JNI?從Java?程序訪(fǎng)問(wèn)本機(jī)代碼時(shí),您會(huì)遇到許多問(wèn)題蹭睡。您會(huì)遇到的三個(gè)最常見(jiàn)的錯(cuò)誤是:

1)無(wú)法找到動(dòng)態(tài)鏈接。它所產(chǎn)生的錯(cuò)誤消息是:java.lang.UnsatisfiedLinkError赶么。這通常指無(wú)法找到共享庫(kù)肩豁,或者無(wú)法找到共享庫(kù)內(nèi)特定的本機(jī)方法。

2)無(wú)法找到共享庫(kù)文件辫呻。當(dāng)用?System.loadLibrary(String?libname)?方法(參數(shù)是文件名)裝入庫(kù)文件時(shí)清钥,請(qǐng)確保文件名拼寫(xiě)正確以及沒(méi)有指定擴(kuò)展名。還有放闺,確保庫(kù)文件的位置在類(lèi)路徑中祟昭,從而確保?JVM?可以訪(fǎng)問(wèn)該庫(kù)文件。

3)無(wú)法找到具有指定說(shuō)明的方法怖侦。確保您的?C/C++?函數(shù)實(shí)現(xiàn)擁有與頭文件中的函數(shù)說(shuō)明相同的說(shuō)明篡悟。

結(jié)束語(yǔ)

從?Java?調(diào)用?C?或?C++?本機(jī)代碼(雖然不簡(jiǎn)單)是?Java?平臺(tái)中一種良好集成的功能。雖然?JNI?支持?C?和?C++匾寝,但?C++?接口更清晰一些并且通常比?C?接口更可取搬葬。正如您已經(jīng)看到的,調(diào)用?C?或?C++?本機(jī)代碼需要賦予函數(shù)特殊的名稱(chēng)艳悔,并創(chuàng)建共享庫(kù)文件急凰。當(dāng)利用現(xiàn)有代碼庫(kù)時(shí),更改代碼通常是不可取的猜年。要避免這一點(diǎn)抡锈,在C++?中,通常創(chuàng)建代理代碼或代理類(lèi)乔外,它們有專(zhuān)門(mén)的?JNI?所需的命名函數(shù)床三。然后,這些函數(shù)可以調(diào)用底層庫(kù)函數(shù)袁稽,這些庫(kù)函數(shù)的說(shuō)明和實(shí)現(xiàn)保持不變勿璃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市推汽,隨后出現(xiàn)的幾起案子补疑,更是在濱河造成了極大的恐慌,老刑警劉巖歹撒,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莲组,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡暖夭,警方通過(guò)查閱死者的電腦和手機(jī)锹杈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)撵孤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人竭望,你說(shuō)我怎么就攤上這事邪码。” “怎么了咬清?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵闭专,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我旧烧,道長(zhǎng)影钉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任掘剪,我火速辦了婚禮平委,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夺谁。我一直安慰自己廉赔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布予权。 她就那樣靜靜地躺著昂勉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扫腺。 梳的紋絲不亂的頭發(fā)上岗照,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音笆环,去河邊找鬼攒至。 笑死,一個(gè)胖子當(dāng)著我的面吹牛躁劣,可吹牛的內(nèi)容都是我干的迫吐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼账忘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼志膀!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鳖擒,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤溉浙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蒋荚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體戳稽,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年期升,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惊奇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片互躬。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖颂郎,靈堂內(nèi)的尸體忽然破棺而出吼渡,到底是詐尸還是另有隱情,我是刑警寧澤乓序,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布诞吱,位于F島的核電站,受9級(jí)特大地震影響竭缝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沼瘫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一抬纸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耿戚,春花似錦湿故、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至皂股,卻和暖如春墅茉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呜呐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工就斤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蘑辑。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓洋机,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親洋魂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绷旗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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