JNI的一个简单实例
本例子使用的操作系统MacOS, 64位JVM。
JNI编写的几个步骤如下:
编写Java代码,并注明native方法:
public class HelloJni { public native void displayHelloJni(); public static void main(String[] args) { HelloJni helloJni = new HelloJni(); helloJni.displayHelloJni(); } static { System.loadLibrary("HelloJniImpl"); } }
其中声明displayHelloJni()方法为本地方法,在static静态块中用System.loadLibrary()加载本地库。
使用javac命令编译此java类:
javac com/xxx/clamaa/jni/HelloJni.java
利用javah命令生成C语言的头文件(.h文件)
javah com.xxx.clamaa.jni.HelloJni
执行完成后,就在执行路径下生成名称为com_xxx_clamaa_jni_HelloJni.h的头文件:
#include <jni.h> /* Header for class com_xxx_clamaa_jni_HelloJni */ #ifndef _Included_com_xxx_clamaa_jni_HelloJni #define _Included_com_xxx_clamaa_jni_HelloJni #ifdef __cplusplus extern "C" { #endif /* * Class: com_yonyou_clamaa_jni_HelloJni * Method: displayHelloJni * Signature: ()V */ JNIEXPORT void JNICALL Java_com_xxx_clamaa_jni_HelloJni_displayHelloJni (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
然后在同级的目录下新建一个HelloJniImpl.cpp文件,这个与HelloJni.java中定义的System.loadLibrary()的文件名一致。
#include "com_xxx_clamaa_jni_HelloJni.h" #include <iostream> #include <jni.h> using namespace std; /* * Class: HelloWorld * Method: displayHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_com_xxx_clamaa_jni_HelloJni_displayHelloJni (JNIEnv *, jobject) { cout << "Hello Jni by clamaa!"; return; }
编译C++代码的时候在MacOS下和在Linux Windows有所不同, 不是编译成.so或者dll, 而是MacOS自己的jnilib. 并且jni.h的目录也比较特殊, 是/System/Library/Frameworks/JavaVM.framework/Headers/,执行的命令g++,
g++ -dynamiclib -o libhellojniimpl.jnilib HelloJniImpl.cpp -framework JavaVM -I/System/Library/Frameworks/JavaVM.framework/Headers
编译完成后,就生成libhellojniimpl.jnilib文件,此时执行结果为:
java com.xxx.clamaa.jni.HelloJni >Hello Jni by clamaa!
生成的文件名称必须为: libhellojniimpl.jnilib,否则抛出异常:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloJniImpl in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857) at java.lang.Runtime.loadLibrary0(Runtime.java:870) at java.lang.System.loadLibrary(System.java:1119) at com.yonyou.clamaa.jni.HelloJni.<clinit>(HelloJni.java:16)
如果设置成带参数的本地方法,那么生成的参数会多一个jstring
Java_HelloWorld_print(JNIEnv *env, jobject obj, jstring str)
与原来的实现方式,调用方式都一样,这样简单的JNI实现就完成了。
如果我们在原来的cpp实现中加入一行抛出异常的代码:
cout << "Hello Jni by clamaa!"; throw 1; return;
那么在执行的过程中也会抛出对应的错误:
java com.yonyou.clamaa.jni.HelloJni >libc++abi.dylib: terminating with uncaught exception of type int >Hello Jni by clamaa!Abort trap: 6