JNI是Java Native Interface的英文縮寫, 中文翻譯爲本地調用, 自從Java 1.1開始就成爲了Java標準的一部分.html
C/C++是系統級的編程語言, 能夠用來開發任何和系統相關的程序和類庫, 可是Java自己編寫底層的應用比較難實現, 使用JNI能夠調用現有的本地庫, 極大地靈活了Java的開發.java
C/C++的效率是目前最好的語言, 可使用C/C++來實現一些實時性很是高的部分. C/C++和Java自己都是很是流行的編程語言, 一些大型軟件中常常使用語言之間的混合編程.linux
鑑於目前網絡上JNI的文章不是特別多, 我將本身的一些總結寫在這裏. 若有錯漏, 歡迎指正!c++
Java調用C/C++大概有這樣幾個步驟編程
下面就完整的介紹一個簡單的Java調用C/C++的例子, 這個例子是來自http://www.ibm.com/developerworks/cn/education/java/j-jni/index.html, 不過其中有一些錯誤, 這個文章是很是不錯的JNI學習資料, 可是很是古老.windows
咱們來編寫一個Sample1的java類數組
1 public class Sample1 { 2 public native int intMethod(int n); 3 public native boolean booleanMethod(boolean bool); 4 public native String stringMethod(String text); 5 public native int intArrayMethod(int[] intArray); 6 7 public static void main(String[] args) { 8 System.loadLibrary("Sample1");//沒有後綴名 9 Sample1 sample = new Sample1(); 10 int square = sample.intMethod(5); 11 boolean bool = sample.booleanMethod(true); 12 String text = sample.stringMethod("Java"); 13 int sum = sample.intArrayMethod(new int[]{1,2,3,4,5,8,13}); 14 15 System.out.println("intMethod: " + square); 16 System.out.println("booleanMethod: " + bool); 17 System.out.println("stringMethod: " + text); 18 System.out.println("intArrayMethod: " + sum); 19 } 20 }
上面有4個native方法, 分別是4種類型的參數, int, boolean, String, int[].bash
其中有一句比較重要, 這句話加載了動態類庫網絡
System.loadLibrary("Sample1");編程語言
在windows下加載的就是Sample1.dll, 在linux下加載的就是Sample1.so.
本文使用的windowws, 因此後面使用Sample1.dll來表示Sample1動態連接庫.
注意: 不能夠在代碼中寫上後綴dll或so. 還要保證Sample1.dll在path路徑中. 這個Sample1.dll是咱們後面須要編譯出來的東西.
4個native方法就是咱們須要用C來實現的方法.
編譯Sample1.java, 使用命令行(windows是cmd, linux下通常是bash)
>javac Sample1.java
能夠看到Sample1.class文件
在命令行中運行
>javah Sample1
能夠在目錄下看到一個新文件Sample1.h
1 /* DO NOT EDIT THIS FILE - it is machine generated */ 2 #include <jni.h> 3 /* Header for class Sample1 */ 4 5 #ifndef _Included_Sample1 6 #define _Included_Sample1 7 #ifdef __cplusplus 8 extern "C" { 9 #endif 10 /* 11 * Class: Sample1 12 * Method: intMethod 13 * Signature: (I)I 14 */ 15 JNIEXPORT jint JNICALL Java_Sample1_intMethod 16 (JNIEnv *, jobject, jint); 17 18 /* 19 * Class: Sample1 20 * Method: booleanMethod 21 * Signature: (Z)Z 22 */ 23 JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod 24 (JNIEnv *, jobject, jboolean); 25 26 /* 27 * Class: Sample1 28 * Method: stringMethod 29 * Signature: (Ljava/lang/String;)Ljava/lang/String; 30 */ 31 JNIEXPORT jstring JNICALL Java_Sample1_stringMethod 32 (JNIEnv *, jobject, jstring); 33 34 /* 35 * Class: Sample1 36 * Method: intArrayMethod 37 * Signature: ([I)I 38 */ 39 JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod 40 (JNIEnv *, jobject, jintArray); 41 42 #ifdef __cplusplus 43 } 44 #endif 45 #endif
咱們能夠看到其中有四個函數聲明, Java_完整類名_方法名, 完整類名包括了包名, 例如demo.Sample1是完整類名, 對應的這裏就是demo_Sample1.
在註釋中咱們能夠看到這樣一個東西 Signature, 這個是方法的簽名. 關於Signature, 下面經過一個表格來講明.
java類型 | Signature | 備註 |
boolean | Z | |
byte | B | |
char | C | |
short | S | |
int | I | |
long | L | |
float | F | |
double | D | |
void | V | |
object | L用/分割的完整類名 | 例如: Ljava/lang/String表示String類型 |
Array | [簽名 | 例如: [I表示int數組, [Ljava/lang/String表示String數組 |
Method | (參數簽名)返回類型簽名 | 例如: ([I)I表示參數類型爲int數組, 返回int類型的方法 |
上面頭文件的第一個函數聲明
JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint);
註釋中的簽名是 Signature: (I)I
在每一個函數的參數列表中都有JNIEnv *和 jobject兩個參數, 這兩個參數稍候說明.
可使用C語言來實現, 也可使用C++來實現, 下面先說說C語言的實現.
#include "Sample1.h" #include <string.h> JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *env, jobject obj, jint num) { return num * num; } JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod (JNIEnv *env, jobject obj, jboolean boolean) { return !boolean; } JNIEXPORT jstring JNICALL Java_Sample1_stringMethod (JNIEnv *env, jobject obj, jstring string) { const char* str = (*env)->GetStringUTFChars(env, string, 0); char cap[128]; strcpy(cap, str); (*env)->ReleaseStringUTFChars(env, string, 0); return (*env)->NewStringUTF(env, strupr(cap)); } JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod (JNIEnv *env, jobject obj, jintArray array) { int i, sum = 0; jsize len = (*env)->GetArrayLength(env, array); jint *body = (*env)->GetIntArrayElements(env, array, 0); for (i = 0; i < len; ++i) { sum += body[i]; } (*env)->ReleaseIntArrayElements(env, array, body, 0); return sum; }
(*env)->GetStringUTFChars()這個方法, 是用來在Java和C之間轉換字符串的, 由於Java自己都使用了雙字節的字符, 而C語言自己都是單字節的字符, 因此須要進行轉換.
JNIEnv *是每一個函數都有的參數, 它包含了不少有用的方法, 使用起來相似Java的反射, 也提供了這樣一個編碼轉換的函數.
GetStringUTFChars()和NewStringUTF(), 第一個是從UTF8轉換爲C的編碼格式, 第二個是根據C的字符串返回一個UTF8字符串.
ReleaseStringUTFChars()是用來釋放對象的, 在Java中有虛擬機進行垃圾回收, 可是在C語言中, 這些對象必須手動回收. 不然可能形成內存泄漏.
函數的名字一眼看到就能夠猜出功能, jni.h中的大部分函數名都是這樣.
若是是C++的話, 這段代碼該怎麼寫?
下面是C++的代碼
#include "Sample1.h" #include <string.h> JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *env, jobject obj, jint num) { return num * num; } JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod (JNIEnv *env, jobject obj, jboolean boolean) { return !boolean; } JNIEXPORT jstring JNICALL Java_Sample1_stringMethod (JNIEnv *env, jobject obj, jstring string) { const char* str = env->GetStringUTFChars(string, 0); char cap[128]; strcpy(cap, str); env->ReleaseStringUTFChars(string, 0); return env->NewStringUTF(_strupr(cap)); } JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod (JNIEnv *env, jobject obj, jintArray array) { int i, sum = 0; jsize len = env->GetArrayLength(array); jint *body = env->GetIntArrayElements(array, 0); for (i = 0; i < len; ++i) { sum += body[i]; } env->ReleaseIntArrayElements(array, body, 0); return sum; }
上述兩端代碼很是類似, 只有一個不一樣點
C代碼: (*env)->GetStringUTFChars(env, string, 0);
C++代碼: env->GetStringUTFChars(string, 0);
C語言中使用的是結構體的函數指針, 而在C++中使用的仍是struct, 咱們知道struct在C++中和class的功能是幾乎同樣的, struct也能夠用來定義類, 因此env在C++中是個類對象的指針.
方法1、使用的是微軟編譯器, 編譯C語言版的dll
>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.c -FeSample1.dll
編譯C++版本的dll
>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.cpp -FeSample1.dll
方法2、用vs新建dll項目
1.新建dll空項目:文件-》新建項目-》Win32控制檯應用程序-》應用程序類型爲DLL-》附加選項爲空項目-》完成
2.項目-》屬性-》c/c++ -》常規-》附加包含目錄是%JAVA_HOME%\include和%JAVA_HOME%\include\win32對應的路徑。
3.有的版本的vs可能須要在預處理器中加入_CRT_SECURE_NO_WARNINGS選項。
運行
>java Sample1
注意: 64位版本的JDK可能會在運行時報錯:
java.lang.UnsatisfiedLinkError: ...Sample1.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
若是您有這樣的錯誤, 請使用32位的JDK來從新運行.
運行結果以下:
intMethod:
25
booleanMethod:
false
stringMethod: JAVA
intArrayMethod:
36