初学JNI,在写HelloWorld的时候遇到的两个问题
(1)无法编译C程序,提示找不到头文件及语法错误
(2)编译成功的dll在Java中调用时报错,找不到方法
经过艰苦卓绝的努力,将解决方案记于此
1、下载eclipse c/cpp(www.eclipse.org)版或安装CDT插件,由于我是直接下载的c版,没有安装过CDT,所以不提供说明
2、安装c/c++编译环境。eclipse本身只是编辑器,语言的编译还是需要编译器自己来做,就像编译Java需要JDK一样。
备选方案有(1)MinGW http://sourceforge.net/projects/mingw/files (2)Cygwinhttp://www.cygwin.com/(3)VC++。一般来说有VC++或者VS的,就不会用eclipse来开发了,所以这个方案被淘汰了,对于前两个方案,由于eclipse帮助里对MinGW讲解比较清楚,而Cygwin没有将具体怎么用,所以我选择了MinGW。安装的默认路径是C:\MinGW,完整安装占磁盘空间为350MB。
3、设置环境变量,新建环境变量 MINGW_HOME,值为 MinGW的安装路径(默认情况下是C:\MinGW)
4、启动eclipse
5、创建Java项目,建一个类,代码如下
- class HelloNative {
- public static native void greeting();
- }
6、生成C头文件
找到刚刚创建的类的class文件,在命令提示符下运行javah HelloNative
注意,请确认jdk/bin目录是否已经添加到path环境变量下,否则可能提示找不到javah。确认如果提示找不到HelloNative类,请将类所在目录添加到classpath环境变量中。基本上javah和java的用法是一样的。
至此我们已经得到了头文件HelloNative.h。什么,没有?请好好找找。
7、创建C项目
Toolchain(工具链,或者叫工具箱?)选择MinGW GCC,没有的话好好检查一下环境变量。OK,直接Finish就行了。
8、导入JNI
如果这时候把刚刚生成的头文件拷到项目中的话会发现各种错误信息
- //(Unresolved inclusion: <jni.h>)
- #include <jni.h>
- //Syntax error
- JNIEXPORT void JNICALL Java_HelloNative_greeting
- (JNIEnv *, jclass);
原因是没有导入jni的包(或者库?姑且这么称呼吧,不知道C里头是怎么叫的)
打开项目的Properties(在项目上右键菜单的最后一项)找到C/C++ General -> Paths and Symbols
Configuration中选择[All configurations](选全部,省的一个一个改)。
Includes页签中的Language选择GNU C,单击Add按钮
单击File system...并找到jdk安装目录,选择其下的include目录,然后确定。
先别着急,到这一步结束Unresolved inclusion: <jni.h>的错误提示没有了,但是下面的语法错误还在,原因是找不到jni_md.h。可是奇怪了,jni_md.h明明就在include/win32下啊,怎么说没有呢?
好吧,这个问题纠结了我很久,后来发现不同于java的到一个jar包,会把其中所有的子包都导进去,坑爹的C居然不会自动导入子包...好吧,重复上面步骤,这次导入jdk/include/win32目录。
好了,大功告成。怪了,怎么还有语法错误提示呢?别管他,创建c文件以后错误就消失了。
9、实现C代码
在这之前,先别急着写C代码。首先把HelloNative.h文件打开,在方法名前加一个下划线,如将Java_HelloNative_greeting改为 _Java_HelloNative_greeting
注:这一步至关重要,否则一会儿在Java里找不到本地方法别怪我。
接下来,创建HelloNative.c文件,并将HelloNative.h中的方法签名复制到C文件中,修改成如下格式
- // in HelloNative.h
- JNIEXPORT void JNICALL _Java_HelloNative_greeting
- (JNIEnv *, jclass);
- // in HelloNative.c
- #include "HelloNative.h"
- JNIEXPORT void JNICALL _Java_HelloNative_greeting(JNIEnv *env, jclass cl) {
- // 这里是方法实现
- printf("Hello World Native\n");
- }
(⊙o⊙)… #include 可以用引号包含,也可以用<>包含,两者引用的优先顺序不同,我不是学C的,说不清楚,自己上网查吧。实现完方法,单击工具栏中的锤子图标进行编译,编译通过就OK了。编译分Debug和Release,看有个帖子说Debug模式编译的dll在java中调会报错,我试了一下,发现可以正常运行,但是原则上说,发行版应该比调试版要好的,至少文件更小一些。至于导出的dll叫什么名字就无所谓了,不喜欢可以改的吗,只要在Java中引用的时候用一样的名字就好了。
gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I/java/include -I/java/include/win32 -shared -o helloNative.dll HelloNative.c
需要将jni.h 和jni_md.h放入到C:\MinGW\include 目录
10、运行测试程序
现在来看看我们的成果吧。将dll拷到java项目的lib目录下(你还有更合适的地方放它吗?)
- public class HelloNativeTest {
- public static void main(String[] args) {
- HelloNative.greeting();
- }
- static {
- System.loadLibrary("HelloNative");
- }
- }
loadLibrary的参数是dll文件的名字,不包含扩展名
运行前先改一下JVM运行参数Run Configurations -> Arguments -> VM arguments填写
-Djava.library.path=.;./lib
好了,Run一下吧,控制台终于打印出熟悉的“HelloWorld”了,不易啊,泪流满面