native關(guān)鍵字說(shuō)明其修飾的方法是一個(gè)原生態(tài)方法秃踩,方法對(duì)應(yīng)的實(shí)現(xiàn)不是在當(dāng)前文件,而是在用其他語(yǔ)言(如C和C++)實(shí)現(xiàn)的文件中。Java語(yǔ)言本身不能對(duì)操作系統(tǒng)底層進(jìn)行訪問(wèn)和操作糟秘,但是可以通過(guò)JNI接口調(diào)用其他語(yǔ)言來(lái)實(shí)現(xiàn)對(duì)底層的訪問(wèn)。
凡是一種語(yǔ)言球散,都希望是純尿赚。比如解決某一個(gè)方案都喜歡就單單這個(gè)語(yǔ)言來(lái)寫(xiě)即可。Java平臺(tái)有個(gè)用戶和本地C代碼進(jìn)行互操作的API蕉堰,稱為Java Native Interface (Java本地接口)凌净。
http://www.cnblogs.com/Alandre/p/4456719.html
創(chuàng)建一個(gè)Java類,里面包含著一個(gè) native 的方法和加載庫(kù)的方法 loadLibrary屋讶。HelloNative.java 代碼如下:
native 關(guān)鍵字告訴編譯器(其實(shí)是JVM)調(diào)用的是該方法在外部定義冰寻,這里指的是C。
public class HelloNative
{static{System.loadLibrary("HelloNative");}public static native void sayHello();
@SuppressWarnings("static-access")
public static void main(String[] args){new HelloNative().sayHello();}}
那個(gè)加載庫(kù)的到后面也起作用皿渗。native 關(guān)鍵字告訴編譯器(其實(shí)是JVM)調(diào)用的是該方法在外部定義斩芭,這里指的是C没卸。如果大家直接運(yùn)行這個(gè)代碼,? JVM會(huì)告之:“A Java Exception has occurred.”控制臺(tái)輸出如下:Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloNative in java.library.path?at java.lang.ClassLoader.loadLibrary(Unknown Source)?at java.lang.Runtime.loadLibrary0(Unknown Source)?at java.lang.System.loadLibrary(Unknown Source)?at HelloNative.(HelloNative.java:5)
運(yùn)行javah秒旋,得到包含該方法的C聲明頭文件.h ? ?->
根據(jù)頭文件约计,寫(xiě)C實(shí)現(xiàn)本地方法 ?->
生成dll共享庫(kù),然后Java程序load庫(kù)迁筛,調(diào)用即可煤蚌。
HelloNative.h文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class HelloNative */
#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:???? HelloNative
* Method:??? sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloNative_sayHello
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
jni.h 這個(gè)文件,在/%JAVA_HOME%include
3细卧、根據(jù)頭文件尉桩,寫(xiě)C實(shí)現(xiàn)本地方法。
這里我們簡(jiǎn)單地實(shí)現(xiàn)這個(gè)sayHello方法如下:
#include "HelloNative.h"
#include
JNIEXPORT void JNICALL Java_HelloNative_sayHello
{printf("Hello贪庙,JNI");}
生成dll共享庫(kù)蜘犁,然后Java程序load庫(kù),調(diào)用即可止邮。在Windows上这橙,MinGW GCC 運(yùn)行如下:
gcc -m64? -Wl,--add-stdcall-alias -I"C:\Program Files\Java\jdk1.7.0_71\include" -I"C:\Program Files\Java\jdk1.7.0_71\include\include\win32" -shared -o HelloNative.dll HelloNative.c
-m64表示生成dll庫(kù)是64位的。然后運(yùn)行HelloNative: java HelloNative
JNI調(diào)用C 流程
JNI是Java本機(jī)接口(JavaNativeInterface)导披,是一個(gè)本機(jī)編程接口屈扎,它是Java軟件開(kāi)發(fā)工具箱(JavaSoftware Development Kit,SDK)的一部分撩匕。JNI允許Java代碼使用以其他語(yǔ)言編寫(xiě)的代碼和代碼庫(kù)鹰晨。Invocation API(JNI的一部分)可以用來(lái)將Java虛擬機(jī)(JVM)嵌入到本機(jī)應(yīng)用程序中,從而允許程序員從本機(jī)代碼內(nèi)部調(diào)用Java代碼止毕。
不過(guò)模蜡,對(duì)Java外部的調(diào)用通常不能移植到其他平臺(tái),在applet中還可能引發(fā)安全異常扁凛。實(shí)現(xiàn)本地代碼將使您的Java應(yīng)用程序無(wú)法通過(guò)100%純Java測(cè)試忍疾。但是,如果必須執(zhí)行本地調(diào)用令漂,則要考慮幾個(gè)準(zhǔn)則:
1.將您的所有本地方法都封裝到一個(gè)類中膝昆,這個(gè)類調(diào)用單個(gè)的DLL。對(duì)每一種目標(biāo)操作系統(tǒng)平臺(tái)叠必,都可以用特定于適當(dāng)平臺(tái)的版本的DLL荚孵。這樣可以將本地代碼的影響減少到最小,并有助于將以后所需要的移植問(wèn)題考慮在內(nèi)纬朝。
2.本地方法盡量簡(jiǎn)單收叶。盡量使您的本地方法對(duì)第三方(包括Microsoft)運(yùn)行時(shí)DLL的依賴減少到最小。使您的本地方法盡量獨(dú)立共苛,以將加載您的DLL和應(yīng)用程序所需的開(kāi)銷(xiāo)減少到最小判没。如果需要運(yùn)行時(shí)DLL蜓萄,必須隨應(yīng)用程序一起提供。
JNI的書(shū)寫(xiě)步驟如下:
a.編寫(xiě)帶有native聲明的方法的Java類
b.使用javac命令編譯編寫(xiě)的Java類
c.使用java-jni ****來(lái)生成后綴名為.h的頭文件
d.使用其他語(yǔ)言(C澄峰、C++)實(shí)現(xiàn)本地方法
e.將本地方法編寫(xiě)的文件生成動(dòng)態(tài)鏈接庫(kù)
以下是一個(gè)在Java中調(diào)用本地C程序的簡(jiǎn)單的例子:
a.編寫(xiě)HelloWorld.java類
class HelloWorld{
publicnativevoid hello();
static{
System.loadLibrary("hello");
}
public static void main(String[] args){
new HelloWorld().hello();
}
}
聲明native方法:如果你想將一個(gè)方法做為一個(gè)本地方法的話嫉沽,那么你就必須聲明改方法為native的,并且不能實(shí)現(xiàn)俏竞。其中方法的參數(shù)和返回值在后面講述绸硕。
Load動(dòng)態(tài)庫(kù):System.loadLibrary("hello");加載動(dòng)態(tài)庫(kù)(我們可以這樣理解:我們的方法hello()沒(méi)有實(shí)現(xiàn),但是我們?cè)谙旅婢椭苯邮褂昧嘶昊伲员仨氃谑褂弥皩?duì)它進(jìn)行初始化)這里一般是以static塊進(jìn)行加載的玻佩。同時(shí)需要注意的是System.loadLibrary();的參數(shù)“hello”是動(dòng)態(tài)庫(kù)的名字。
b.編譯
javac HelloWorld.java
c.生成.h文件
javah -jni HelloWorld
示例比如說(shuō) 類文件在D:\project\sparkStreamingLearn\src\main\java\TestNative\HelloWorldnative.java
使用javah 時(shí)注意執(zhí)行位置是源代碼目錄 D:\project\sparkStreamingLearn\src\席楚,
classpath是 載入類的路徑 是 D:\project\sparkStreamingLearn\src\main\java\
對(duì)應(yīng)的類為:TestNative包下的HelloWorldnative.java? -jni 路徑是包名+類名 即 TestNative\HelloWorldnative.java
-d 輸出目錄(可以任意指定) 即TestNative_HelloWorldnative.h文件的輸出目錄 當(dāng)前可以指定為D:\project\sparkStreamingLearn\src\main\java\TestNative\
結(jié)果為: D:\project\sparkStreamingLearn\src\main\java\TestNative\TestNative_HelloWorldnative.h
我們要開(kāi)始寫(xiě)javah的命令咬崔,以便生成對(duì)應(yīng)的C語(yǔ)言頭文件
D:\我的文檔\workspace\PrepareForExam\src>javah -classpath D:\我的文檔\workspace\PrepareForExam\bin -d d:/ -jni
com.example.myclass.jni_test
其中java中各個(gè)命令的意思是
-classpath <路徑> 用于裝入類的路徑
-d <目錄> 輸出目錄
-jni 生成 JNI樣式的頭文件(默認(rèn))
注意到以上我們命令中指定的路徑
注意到我們的命令符的執(zhí)行位置是源代碼目錄”D:\我的文檔\workspace\PrepareForExam\src”
-classpath? 后面的路徑是指包”com.example.myclass”所在的根路徑
-jni 后面的路徑是包名+類名
生成內(nèi)容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:???? HelloWorld
* Method:????hello
* Signature: ()V
*/
JNIEXPORT void JNICALLJava_HelloWorld_hello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
第一個(gè)參數(shù)是調(diào)用JNI方法時(shí)使用的JNI Environment指針。第二個(gè)參數(shù)是指向在此Java代碼中實(shí)例化的Java對(duì)象HelloWorld的一個(gè)句柄烦秩。其他參數(shù)是方法本身的參數(shù)
d.c實(shí)現(xiàn)
#include
#include "HelloWorld.h"
#include
JNIEXPORT void JNICALLJava_HelloWorld_hello(JNIEnv *env,jobject obj){
printf("Hello World!\n");
return;
}
其中垮斯,第一行是將jni.h文件引入(在%JAVA_HOME%\include目錄下),里邊有JNIEnv和jobject的定義闻镶。
e.編譯c實(shí)現(xiàn)
cl -- vs201X 的安裝目錄下 Common7/Tools/Shortcuts/VS2013開(kāi)發(fā)人員命令提示? 開(kāi)始cl 命令行? c/c++ 編譯器 ?這里以在Windows中為例甚脉,需要生成dll文件。在保存HelloWorldImpl.c文件夾下面铆农,使用VC的編譯器cl成。是-I? 搜索其后添加所需文件所在的目錄 注意需要添加"" 即"java_home%\include" -LD? 是創(chuàng)建.dll
這里以在Windows中為例狡耻,需要生成dll文件墩剖。在保存HelloWorldImpl.c文件夾下面,使用VC的編譯器cl成夷狰。
cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll
注意:生成的dll文件名在選項(xiàng)-Fe后面配置岭皂,這里是hello,因?yàn)樵贖elloWorld.java文件中我們loadLibary的時(shí)候使用的名字是hello沼头。當(dāng)然這里修改之后那里也需要修改爷绘。另外需要將-I%java_home%\include -I%java_home%\include\win32參數(shù)加上,因?yàn)樵诘谒牟嚼锩婢帉?xiě)本地方法的時(shí)候引入了jni.h文件进倍。
6) 運(yùn)行程序
javaHelloWorld就ok了土至!
javac? 出現(xiàn)錯(cuò)誤 不是內(nèi)部或外部命令,也不時(shí)可運(yùn)行的程序? 修改path 添加 %Java_home%/bin;%Java_home%/jre/bin;
javac 出現(xiàn)中文亂碼 修改方法: javac -encoding "UTF-8" HelloWorldnative.java