人們可以找到很多關於Solaris和Windows中JNI的教程,而且在那些例子中C++出現得也不是那麼頻繁,出於這個原因,我通過兩個例子來說明一下其使用。
對於下面的程序,要求你使用的SunJDK,而不是OpenJDK(在文章後面我會再次提到這點)。
第一個例子是從Java中傳遞一個字符串到native method中,在native method中打印個性化的Helllo World。
public class HelloWorld {
public native void sayHi(String name);
static {
System.loadLibrary("nativehw");
}
public static void main(String[] args) {
HelloWorld h = new HelloWorld();
h.sayHi("Night Rider");
}
}
在這裏先說明一下,我們打算通過nativehw庫來調用本地方法sayHi。
編譯的我們類:
javac HelloWorld.java
然後生成本地方法的頭文件:
javah -jni HelloWorld
接着實現HelloWorld.h聲明的方法:
(注,注意第一行的include。原文中這段代碼不是很完整,爲保持作者原意,故此這裏我也不做相關修改)
#include
#include "HelloWorld.h"
using namespace std;
JNIEXPORT void JNICALL Java_HelloWorld_sayHi(JNIEnv *env, jobject o, jstring s)
{
const char *str = env->GetStringUTFChars(s, 0); cout << "Hello " << str << endl;
env->ReleaseStringUTFChars(s, str);
}
由於在C++中沒有像Java那樣的垃圾收集器,所以我們需要做一些清理工作。(譯者注:這個是作者的建議,在本段代碼中並未體現出來)
寫好後保存爲HelloWorld.cpp然後進行編譯:
g++ -shared -I/usr/lib/j2sdk1.6-sun/include -I/usr/lib/j2sdk1.6-sun/include/linux HelloWorld.cpp -o libnativehw.so
注意不同系統環境的include目錄的路徑可能會有所不同,生成前面提到的nativehw庫。
我們像這樣運行的例子:
java -Djava.library.path=. HelloWorld
預期的輸出結果是“Hello Night Rider”。
第二個例子將展示用JNI處理一些異常,同樣我們先開始編寫Java代碼:
public class MoreStuff {
public native double div(double x, double y) throws Exception;
static {
System.loadLibrary("whatever");
}
public static void main(String[] args) {
MoreStuff m = new MoreStuff();
try {
System.out.println("7.0 / 2.0 = " + m.div(7.0, 2.0));
System.out.println("0.0 / 0.0 = " + m.div(0.0, 0.0));
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
編譯並且生成頭文件:
javac MoreStuff.java javah -jni MoreStuff
實現的聲明的發發的C++代碼如下:
NIEXPORT jdouble JNICALL Java_MoreStuff_div (JNIEnv *env, jobject o, jdouble x, jdouble y)
{
if(0.0 == y){
jclass e = env->FindClass("java/lang/IllegalArgumentException");
if (e == 0) {
return -3.14;
}
env->ThrowNew(e,"Zero argument exception.");
env->DeleteLocalRef(e);
}
return x/y;
}
建議使用更健壯的異常處理機制來處理異常(譯者注:這也是作者的建議,並未在代碼中體現出來)。
g++ -shared -I/usr/lib/j2sdk1.6-sun/include -I/usr/lib/j2sdk1.6-sun/include/linux MoreStuff.cpp -o libwhatever.so
這裏是執行的操作和輸出的結果:
java -Djava.library.path=. MoreStuff 7.0 / 2.0 = 3.5 Zero argument exception.
爲了讓編譯通過,可能需要編輯JDKHOME/include/jni.h,包括相關文件或目錄(譯者注:此即爲上文中爲何沒有在代碼中包含這個的原因):
#include "jni_md.h"
或着這樣:
#include "linux/jni_md.h"
(譯者:這裏我認爲作者的意思是應該是運行Java例子時使之包括相關的路徑,尤其要注意jni.h和jni.md.h,這兩個頭文件一定不能漏掉。建議在g++中添加-I標誌來特別標明這幾個include路徑)。
這裏是我安裝的JDK路徑的描述,雖然jni.h也是OpenJDK源碼的一部分,但是我卻沒有在Ubuntu8.04的OpenJDK安裝路徑中找到它,所以這也是我強調使用SunJDK的原因。