JNI学习笔记(三)——HelloWorld

来源:互联网 发布:抢月饼javascript 编辑:程序博客网 时间:2024/05/19 16:32



概述


helloworld作为大多编程语言和平台的入门程序,JNI也不例外。下图展示了java程序调用JNI的例子:HelloWorld。编写这样一个程序,需要以下几个步骤,大多调用JNI的java程序也都需要有以下几个步骤:

1)创建声明了native 方法的类(HelloWorld.java)。

2)使用javac编译源文件,生成类文件(HelloWorld.class)。

3)使用javah -jni生成C头文件(HelloWorld.h)。

4)用C、C++实现native方法(HelloWorld.c、HelloWorld.cpp)。

5)在主机环境中用C、C++编译器将该实现编译生成native库文件(HelloWorld.dll、libHelloWorld.so)。

6)在java运行时中运行改程序。HelloWorld.class和(HelloWorld.dll或者libHelloWorld.so)都被导入到运行时中。



声明native方法


用java编程语言开始编写这个程序。定义一个HelloWorld类:

class HelloWorld

{

public static void main(String[] args)

{

new HelloWorld.print();

}


public native void print();


static

{

System.loadLibrary("HelloWorld");

}

}


声明native方法时,必须包含修饰字:native,同时在java编程语言中不应该实现该方法。在native方法被调用前,native库必须被导入。我们在静态初始化中导入native库。java VM将在调用HelloWorld类中的任何方法前,先自动地运行静态初始化,这样就保证了在native方法print()在native库被导入之后才被调用。


在定义的main方法中,像调用其他普通的java语言的方法一眼调用native方法。


System.loadLibrary中的参数是库的名字,库的名字是指库文件中除去前缀(linnux)和后缀的部分,如WIN32的HelloWorld.dll文件、LINUX的libHelloWorld.so文件,其库名都是HelloWorld。



编译HelloWorld类


在完成HelloWorld.java文件之后,使用javac编译器编译生成java类:

javac HelloWorld.java

这条命令会在当前目录下生成一个HelloWorld.class文件。



创建native方法的头文件


通过以下命令可以自动生成头文件:

javah -jni HelloWorld

更常用的形式是:javah -d ./OutPath -classpath ./ClassPath -jni PackageName.ClassName


在头文件HelloWorld.h中,会有一个函数声明:

JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *, jobject);

该函数的实质就是:

1)JNIEXPORT声明该接口为动态库导出接口。

2)JNICALL声明了函数参数的入栈方式。

3)函数名为包名+类名+native方法名。

例如,如果包名是com.jni.demo,类名是HelloWorld,native方法名print,则该函数名要改为:Java_com_jni_demo_HelloWorld_print。

4)函数参数,每个函数都会额外添加两个参数:JNIEnv *,jobject或者jclass(static native方法时)。具体作用以后再讲。



写native方法的实现


javah生成的JNI类型的头文件,只是帮助你用C、c++编写实现native方法。例如Java_HelloWorld_print()可以如下实现,就如你在实现其他C、C++函数一样:

#include <jni.h>    /* 必须包含该头文件,它是jni的关键 */

#include <stdio.h>


JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj)

{

printf("Hello World!\n");

return;

}



编译源文件并且创建native库


回忆一下在创建HelloWorld类的时候,有一行代码:

System.loadLibrary("HelloWorld");


必要的C、C++代码已经完成了,现在需要将它们编译成native库。不同的操作系统,用不同的方式来生成native库。

1)linux(Solaris)

cc -G -I/java/include -I/java/include/solaris HelloWorld.c -o libHelloWorld.so

-G选项,是让编译器生成一个共享库文件。

2)Win32,用Microsoft Visual C++编译生成DLL文件:

cl -Ic:\java\include -Ic:\java\include\win32 -MD -LD HelloWorld.c -FeHelloWorld.dll

-MD选项,保证HelloWorld.dll是和Win32多线程的C库一起链接的。

-LD选项,是让编译器生成一个DLL文件。



运行该程序


此刻,我们已经有了运行该程序所必要的两个组件:native库和java class。我们可以通过以下命令来运行改程序:

java HelloWorld

我们将可以看到这样的输出:

Hello World!


在运行前,有必要为程序设置正确的native库的路径。native库的路径是一个目录的列表,java VM在导入native库的时候会搜索该列表。如果没有设置,或者没有正确的设置native库的路径,在运行时,将会看到类似于以下的错误信息:

java.lang.UnstatisfieldLinkError: no HelloWorld in library path

at java.lang.Runtime.loadLibrary(Runtime.java)

at java.lang.System.loadLibrary(System.java)

at HelloWorld.main(HelloWorld.java)


保证native库时驻留在库路径中的一个目录中。

1)如果是linux,设置该路径到环境变量LD_LIBRARY_PATH中:

LD_LIBRARY_PATH=.

export LD_LIBRARY_PATH


2)如果是Windows,则保证HelloWorld.dll是在当前目录,或者是在一个被列在PATH环境变量中的一个目录下。

3)在对于java 2 SDK1.2版本,也可以在java命令中指定native库的路径为系统的属性:

java -Djava.library.path=. HelloWorld

-D选项,设置了java平台的系统属性。设置了java.library.path的属性为“.”,只是java VM在当前目录下寻找native库。












原创粉丝点击