首先要声明的是,这个例子的代码是在网上到处都有,我在Fedora Core 6, JDK1.5.0_04下调试成功。写这篇文章的目的是记录一下JNI程序的编译和运行步骤。
程序的流程
在JAVA程序中输入用户名“John”,然后在调用C程序中的方法,显示“Hello, John”.
首先编写Hello.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Hello { static { try { System.loadLibrary("hello"); } catch(UnsatisfiedLinkError e) { System.err.println( "Cannot load hello library: "+ e.toString() ); } } public Hello(){} public native void SayHello(String strName); }
|
编译该文件,生成 Hello.class
javac Hello.java
运行javah,产生头文件Hello.h
javah Hello
Hello.h中的内容不要做修改,先打开它,看看本地方法是怎么声明的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Hello */ #ifndef _Included_Hello #define _Included_Hello #ifdef __cplusplus extern "C" { #endif /* * Class: Hello * Method: SayHello * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_Hello_SayHello (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
|
然后按照Hello.h中的声明方法,编写本地方法的实现文件Hello.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <jni.h> #include "Hello.h" #include <stdio.h> JNIEXPORT void JNICALL Java_Hello_SayHello (JNIEnv * env, jobject arg, jstring instring) { char *str = (char *)(*env)->GetStringUTFChars( env, instring, NULL); printf("Hello, %s ", str); (*env)->ReleaseStringUTFChars( env, instring, NULL); }
|
编译和加载库文件
其实以上的步骤并没有什么难度,只是一个模板流程而已,我认为对于初学者来说,让人头疼的部分还是编译和加载库文件,下面就是这一关键的步骤。为了编译方便,我先写了一个makefile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| libhello.so:Hello.o makefile gcc -Wall -rdynamic -shared -o libhello.so Hello.o Hello.o:Hello.c Hello.h gcc -Wall -c Hello.c -I./ -I/usr/java/jdk1.5.0_04/include -I/usr/java/jdk1.5.0_04/include/linux cl: rm -rf *.o *.so
|
(注意:以“#”开头的部分是注释,你可以全部删除之, gcc和rm等命令的开头必须用tab键做一个空格)
运行一下命令来编译我们的库文件:
#sudo chmod +x makefile
#make
gcc -Wall -c Hello.c -I./ -I/usr/java/jdk1.5.0_04/include -I/usr/java/jdk1.5.0_04/include/linux
Hello.c:15:2: warning: no newline at end of file
gcc -Wall -rdynamic -shared -o libhello.so Hello.o
那个警告信息我们可以不管,这样的话就在当前目录下生成了库文件:libhello.so
再写一个类来调用我们的本地方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class ToSay { public static void main(String argv[]) { ToSay say = new ToSay(); } public ToSay() { Hello h = new Hello(); h.SayHello("John"); } }
|
编译该文件:
javac ToSay.java
最后就是配置java的运行环境了,使用JNI加载库文件时,必须修改环境变量:LD_LIBRARY_PATH,这样java程序才能准确的找到库文件的位置,在网上有很多关于修改LD_LIBRARY_PATH的方法,但是到我这里都行不通。
我的处理方法是这样的,为运行方便我们先写一个运行脚本,不妨就叫它run.sh:
1 2 3 4 5
| #!/bin/bash export LD_LIBRARY_PATH=.:$PATH:$LD_LIBRARY_PATH java ToSay 注意,我把PATH也加进来了。这样库文件的路径就包括:当前目录,系统的PATH路径以及原来的LD_LIBRARY_PATH路径,在程序执行时,它就会按照以上的顺序来搜索需要的库文件。
|
用以下的命令来执行这个脚本:
sudo chmod +x run.sh
./run.sh
运行的结果是:
# ./run.sh
Hello, John
#
Have a nice day!!!