咱們能夠看到其中有四個函數聲明, Java_完整類名_方法名, 完整類名包括了包名, 例如demo.Sample1是完整類名, 對應的這裏就是demo_Sample1.html
在註釋中咱們能夠看到這樣一個東西 Signature, 這個是方法的簽名. 關於Signature, 下面經過一個表格來講明.java
java類型編程 |
Signature數組 |
備註函數 |
booleanui |
Z編碼 |
|
bytespa |
B指針 |
|
charorm |
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語言的實現.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#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++的代碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#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++中是個類對象的指針.
編譯和運行
這裏使用的是微軟編譯器, 編譯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
運行
>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 |
源代碼下載: Sample1.zip
運行其中的build&run.bat文件便可, 若有錯誤請根據實際狀況修改其中的一些參數.
DLL工程文件VC6.0和VS2010的: VC6.0&VS2010.zip
參考文獻:
Scott Stricker, 用 JNI 進行 Java 編程, http://www.ibm.com/developerworks/cn/education/java/j-jni/section2.html
JDK 6u30 docs, Java Native Interface Specification, Chapter 3 JNI Types and Data Structures, Type Signatures.