人们可以找到很多关于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的原因。