JNI
是Java Native Interface
的縮寫,是Java
平臺的本地調用,從Java1.1
就成爲了Java
標準的一部分,它容許Java
代碼和其它語言的代碼進行互相調用,只要調用約定支持便可,尤爲和C/C++
的互相調用。html
雖然使用Java
與本地編譯的代碼進行交互,會喪失平臺的可移植性,可是在特定狀況下,這些問題是能夠接受的,如:java
1.使用一些舊的庫
2.須要操做系統交互
3.提升程序的性能c++
Java
是經過定義native
方法,而後用其它語言實現該方法,最後在Java
運行時,動態地加載該方法實現,經過調用native
的方法,進而實現Java
的本地調用。編程
JVM
封裝了各類操做系統的差別性,提供了jni
技術,使得開發中能夠經過Java
程序調用到操做系統的函數,進而與其它技術進行交互。下圖是Linux
平臺jni
的調用流程。Java
應用程序經過jni
接口調用動態連接庫*.so
,來實現jni
的功能。數組
Java基本數據類型與C語言基本數據類型的對應架構
1) GetStringUTFLength
以字節爲單位返回字符串的UTF-8
長度函數
// jsize (JNICALL *GetStringUTFLength)(JNIEnv *env, jstring str) int len = (*env)->GetStringUTFLength(env, str);
2) GetStringUTFChars
返回指向字符串的UTF-8
字符數組的指針。該數組在被ReleaseStringUTFChars()
釋放前將一直有效性能
// const char* (JNICALL *GetStringUTFChars)(JNIEnv *env, jstring str, jboolean *isCopy) const char *buf = (*env)->GetStringUTFChars(env, str, NULL);
當isCopy
爲JNI_FALSE
,不要修改返回值,否則將改變java.lang.String
的不可變語義。 通常會把isCopy
設爲NULL
,不關心Java VM
對返回的指針是否直接指向java.lang.String
的內容spa
3) ReleaseStringUTFChars
通知虛擬機平臺相關代碼無需再訪問utf
,utf
參數是一個指針,可利用GetStringUTFChars()
得到操作系統
// void (JNICALL *ReleaseStringUTFChars)(JNIEnv *env, jstring str, const char* chars) (*env)->ReleaseStringUTFChars(env, str, buf);
4) NewStringUTF
利用UTF-8
字符數組構造新java.lang.String
對象
// jstring (JNICALL *NewStringUTF)(JNIEnv *env, const char *utf) (*env)->NewStringUTF(env, "hello");
jni.h
下面介紹jni
的具體實現步驟,主要是經過Java
程序調用C
方法,跑通整兒jni
的調用流程。
編寫Java
的Hello
類,定義一個native
的本地方法
public class Hello { public native static String sayHello(String name); static { System.load("你的*.so的絕對路徑"); } public static void main(String[] args) { Hello hello = new Hello(); String ret = hello.sayHello("kelvin"); System.out.println(ret); } }
使用javac
命令進行編譯
# javac Hello.java
這是關鍵的一步,主要是生成本地方法簽名,依賴的是上一步的class
文件,
# javah -jni Hello
若是你的java
源文件有包名,在生成*.sh
的時候,也要帶包名轉化的路徑,即用classpath
指定包所在的路徑,否則在最後調用時,會報錯:UnsatisfiledLinkError
// java源文件包名 package kelvin.Java.dynamicso; // 編譯時指定classpath # javah -classpath /Users/kelvin/Documents -jni kelvin.Java.dynamicso.Hello
在生成的Hello.h
頭文件中,有須要實現的本地方法名,在實現時,要記得指定參數名稱
#include <stdio.h> #include "Hello.h" JNIEXPORT jstring JNICALL Java_Hello_sayHello(JNIEnv *env, jclass jc, jstring name) { const char *buf; buf = (*env)->GetStringUTFChars(env, name, NULL); if (NULL == buf) { return NULL; } printf("%s\n", buf); (*env)->ReleaseStringUTFChars(env, name, buf); return (*env)->NewStringUTF(env, "hello"); }
因爲是Linux
平臺,須要製做後綴是.so
的動態庫,其中,須要指定jni.h
的路徑,必要時還須要jni_md.h
的路徑,該文件在jdk
的目錄中
# gcc -c -fPIC -I/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include Hello.c -o Hello.o # gcc -shared Hello.o -o libhello.so
加載動態庫有2種方式:
1)load():須要指定庫的絕對路徑
2)loadLibrary():須要指定庫的相對路徑,即java.lib.path
如今jni
調用的一切都準備好了,進行最後的調用,有正常的打印輸出,代表jni
正常調用了
# java Hello kelvin hello
以上就是Linux
平臺的jni
調用方式,下一篇介紹Windows
平臺的jni
調用方式。。。
參考資料
JNI之String類型
Jni編程(三)c/c++ 獲取java字符串,以及java 獲取c/c++建立的對象
一天掌握Android JNI本地編程 快速入門