这是一个经典的helloworld程序,此程序本身并没有什么功能,但是其意义在于抛砖引玉。了解了JNI的机理,对于那些习惯于C程序的人很重要,在处理底层的读写等操作就可以在UI层用java轻而易举地实现,就
如同C程序中调用一个系统调用那么简单,下面分步叙述。
一、创建最上层的java程序
首先打开eclipse,创建一个java工程,并在工程中添加一个类,类名为helloworld.java,如图所示:
打开java源码helloworld.java,编写代码,实现c语言中的printf功能,代码如下:
Print()函数的声明中,关键字native标明此方法是一个“本地”方法或者“原生”方法,即从c/c++库中调用的方法。
Main()函数必须为static,否则运行时会报错,找不到main,原因不详。
Static段中的loadLibrary提供了此程序运行时需要加载的c动态链接库***.so,print()的实现就在此库中。
二、
编译helloworld.java
编译helloworld.java的方法有两种,一种是在shell中利用javac编译,另一种在eclipse中,直接保存。
对于第一种方法,直接告诉javac编译器需要编译的文件即可,命令如下:
Javac helloworld.java
生成的helloworld.class就在$(shell pwd)下。
第二种方法,eclipse会在工程目录下的bin目录下生成helloworld.class。
三、
生成JNI头文件helloworld.h
利用javah命令生成。Javah的命令格式如下:
javah –jni –classpath <classpath> <classfile>
例如我的helloworld.class文件是由eclipse生成的,因此其绝对路径是:
/root/Project/jni/helloworld/bin/helloworld.class
我当前所在目录为/root/Project/jni/helloworld/
上面的命令就可以写成:javah –jni –classpath bin helloworld
命令执行后再$(shell pwd)下生成一个文件:helloworld.h,其内容如下:
在这个文件中提供了java到c的接口
JNIEXPORT void JNICALL Java_helloworld_print(JNIEnv *, jobject);
另外,jni.h中定义了各种从java到c的类型转换,宏定义等。
接着我在工程根目录下创建了一个目录include,将生成的头文件mv到include目录下。
四、
创建helloworld.c并编译生成动态链接库helloworld.so
首先编写代码如下(推荐在vim或者其他可视化编辑软件中):
由图中可见,此处就是最上层的helloworld.java代码中的方法的实现,在此实现中没有用到系统提供的两个参数,但是此2个参数非常重要。第一个参数代表此共享库运行的环境,即java虚拟机,在虚拟机中奖上层的java类型转换为C类型。第二个参数类似于C++中的this对象,C代码要通过此函数来改变对象中某些变量的值。
接下来要做的就是将此C代码编译成共享链接库文件,可以直接在shell里完成,或者为了便于管理,通过GNU make。
编译时需要定位jni.h所在路径,locate,结果如下:
由于我当前使用的ndk是~/Project/android-ndk-r4b,并且我想让程序运行在宿主机上,于是选择第4条或者第6条,我选第6条,因此在gcc的flags里应该添加此路径。
为了方便起见,我在helloworld根目录下创建一个makefile,采用递归调用Makefile,深入到各目录中进行操作,对各子目录下的Makefile统一管理:
源文件所在目录的Makefile如下:
使用make命令在工程根目录下编译之后,在工程根目录下的lib目录中生产了helloworld.so的共享库文件:
五、
检验程序正确性
运行程序有两个方法:shell中运行和eclipse中运行。
1.
shell中运行
运行jni程序需要告诉java两个参数,即classpath和共享库的位置。
classpath可以通过设置环境变量:
export CLASSPATH=$CLASSPATH:/root/Project/workspace/jni/helloworld/bin/
而共享库的位置在命令行中给出即可:
Java –Djava.library.path=’lib/’ helloworld
运行结果如下:
2.
eclipse中运行
需要为java虚拟机设置共享库路径,打开run configurations,切换到第二个选项卡arguments,在虚拟机参数输入框中输入
-Djava.library.path=/root/Project/workspace/jni/helloworld/lib/
如图所示:
之后运行,运行结果如下:
六、 补充说明
我的工程根目录:/root/Project/workspace/jni/helloworld/
如果用eclipse编写代码并运行的话,需要为eclipse安装CDT插件,这样才可以支持c/c++。对于这个程序,如果不使用eclipse,可能反而更简单一些。
另外,环境变量的设置也很重要,要根据自己系统中jdk的路径设置环境变量,设置方法可以上网搜索。