本地方法(JNI)——从java 程序中调用C函数
【0】README
1) 本文部分文字描述 轉(zhuǎn)自 core java volume 2 , 旨在理解 本地方法——從java 程序中調(diào)用C函數(shù) 的基礎(chǔ)知識(shí) ;
2) for source code, please visit https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter12/javaCassFuncOfC
【1】本地方法相關(guān)
1)本地代碼定義: 原則上說, 100% 純 java 的解決方案是非常好的, 但有時(shí)你也會(huì)想要編寫或使用其他語(yǔ)言的代碼, 這種代碼就稱為本地代碼;
2)我們建議只有在必需的時(shí)候才使用本地代碼, 特別是在以下三種情況的時(shí)候(cases):
- c1)你的應(yīng)用需要訪問的系統(tǒng)特性和設(shè)備通過java 平臺(tái)無法實(shí)現(xiàn);
- c2)你已經(jīng)有了大量的測(cè)試過 和 調(diào)試過的 用另一種語(yǔ)言編寫的代碼, 并且知道如何將其導(dǎo)出到所有的目標(biāo)平臺(tái)上;
- c3)通過基準(zhǔn)測(cè)試, 你發(fā)現(xiàn)所編寫的 java 代碼比用另一種語(yǔ)言編寫的等價(jià)代碼要慢很多;
3)java 本地接口(JNI): java 平臺(tái)有一個(gè)用于和本地C 代碼進(jìn)行交互操作的 API, 稱為java 本地接口(JNI);
【2】從java 程序中調(diào)用C函數(shù)
1) 關(guān)鍵字native: java 使用 native 表示本地方法;
class HelloNative { public static native void greeting(); }2)本地方法: 既可以是靜態(tài)的,也可以是非靜態(tài)的;
3)為了實(shí)現(xiàn)本地代碼: 需要編寫一個(gè)C 函數(shù),你必須完全按照 java 虛擬機(jī)預(yù)期的那樣來命名這個(gè)函數(shù)。 其規(guī)則是(rules):
r1)使用完整的java 方法名:
如 HelloNative.greeting; 如果屬于某個(gè)包還要添加包名, 如 com.corejava.HelloNative.greeting;
r2)用下劃線替換掉所有的句號(hào), 并加上 Java_前綴, 如:
Java_com_corejava_HelloNative_greeting;
r3)如果類名含有非 ASCII 字母或數(shù)字:
如:’_’, ‘$’ 或是 大于 ‘\u007F’ 的 unicode 字符,用_0xxxx 來替代它們, xxxx 是該字符的unicode 值的 4個(gè)十六進(jìn)制數(shù)序列;
Attention)
- A1)如果你重載本地方法, 也就是說, 你用相同的名字提供多個(gè)本地方法, 那么你必須在名稱后面附加兩個(gè)下劃線, 后面再加上已編碼的參數(shù)類型;
- A2) 看個(gè)荔枝: 如果你有一個(gè)本地方法 greeting 和 本地方法 greeting(int repeat), 那么第一個(gè)稱為 Java_HelloNative_greeting__, 第二個(gè)稱為 Java_HelloNative_greeting_I;
- A3)javah程序:沒有人完成這些手工操作, javah 自動(dòng)生成函數(shù)名;
4)java程序調(diào)用C函數(shù)的實(shí)現(xiàn)steps:
(javah導(dǎo)出類的頭文件可能拋出異?!猨ava.lang.IllegalArgumentException: Not a valid class name, for solution, please visit http://blog.csdn.net/pacosonswjtu/article/details/50615988)
step1)用 javah 生成函數(shù)名:
對(duì)以上代碼的分析(Analysis):
- A1) 這個(gè)頭文件包含了函數(shù) Java_HelloNative_greeting 的聲明;
- A2) 字符串 JNIEXPORT 和 JNICALL 是在頭文件 jni.h 中定義的, 他們?yōu)槟切﹣碜詣?dòng)態(tài)裝載庫(kù)的導(dǎo)出函數(shù)標(biāo)明了 依賴于編譯器的說明符;
step2)現(xiàn)在,需要將 函數(shù)原型從頭文件中 復(fù)制到源文件中, 并且給出函數(shù)的實(shí)現(xiàn)代碼(新建 HelloNative.c);
step3) 將本地C 代碼編譯到一個(gè)動(dòng)態(tài)裝載庫(kù)中, 具體方法依賴于編譯器;
step3.1) Linux 下的 GNU C 編譯器:
使用如下命令:(1) gcc -fPIC -I jdk/include -I jdk/include/linux -shared -o libHelloNative.so HelloNative.c 或
(2.1) gcc -c -I /usr/java/jdk1.7/include/ -I /usr/java/jdk1.7/include/linux/ HelloNative.c
(2.2) gcc -shared -fPIC -o HelloNative.so HelloNative.ostep3.2)使用windows 下的微軟編譯器:
命令是: cl -I jdk/include -I >jdk/include/win32 -LD HelloNative.c -FeHelloNative.dll
step3.3)通過cygwin 環(huán)境下 將本地C 代碼編譯到動(dòng)態(tài)裝載庫(kù)中:
gcc -mno-cygwin -D __int64=”long long” -I jdk/include/win32 -shared -Wl, –add-stdcall-alias -o HelloNative.dll HelloNative.c
step4) 最后,我們要在程序中添加 一個(gè)對(duì) System.loadLibrary 方法的調(diào)用。為了確保虛擬機(jī)第一次使用該類之前就會(huì)裝載這個(gè)庫(kù), 需要使用 靜態(tài)初始化代碼塊。 如下所示:
Attention)如果運(yùn)行在 Linux 下, 必須把當(dāng)前目錄添加到庫(kù)路徑中。
A1)實(shí)現(xiàn)的方式可以是通過設(shè)置 LD_LIBRARY_PATH 環(huán)境變量:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
A2)或者是設(shè)置 java.library.path 系統(tǒng)屬性:
java -Djava.library.path=. HelloNativeTest
Conclusion) 總之,遵循下面的steps就可以將一個(gè)本地方法鏈接到 java 程序中:
step1)在 java類中聲明一個(gè)本地方法;
package com.corejava.chapter12;public class HelloNative { public static native void greeting(); }step2) 運(yùn)行javah 以獲得包含該方法的 C 聲明的頭文件;
step2.1)編譯文件: javac com/corejava/chapter12/HelloNative.java
- step2.2)javah生成該類的頭文件: javah com.corejava.chapter12.HelloNative
step3) 用 C 實(shí)現(xiàn)該本地方法:
- step3.1)將 相應(yīng)的方法原型(返回類型,方法名,參數(shù)列表) copy 到 .c 文件中,并實(shí)現(xiàn)該方法):
step3.2)在linux環(huán)境下, 將本地C 代碼編譯到動(dòng)態(tài)裝載庫(kù)中
gcc -c -I /usr/java/jdk1.7/include/ -I /usr/java/jdk1.7/include/linux/ HelloNative.c
gcc -shared -fPIC -o libHelloNative.so HelloNative.ostep4)將代碼置于 共享類庫(kù)中;
step5) 在 java 程序中 加載該類庫(kù)(java類中的main調(diào)用C函數(shù))
- Warning) 這里運(yùn)行可能出錯(cuò),throw exception : java.lang.UnsatisfiedLinkError: no yourClassName in java.library.path , 這也就是 為什么本文先打印出了 java.library.path, 然后將 libHelloNative.so 添加到該共享庫(kù)中的原因;當(dāng)然了, for detailed spec , you can also refer to http://blog.csdn.net/pacosonswjtu/article/details/50618033
Conclusion) 最后的工作目錄文件列表如下:
總結(jié)
以上是生活随笔為你收集整理的本地方法(JNI)——从java 程序中调用C函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样设置接收邮件规则(outlook怎么
- 下一篇: java JNI调用C语言动态链接库(j