JNI实战

JNI即是Java Native Interface,主要用来Java和其他语言之间的交互,多半是和平台依赖的调用、其他高级语言的库甚至是低级语言交互。相信大家平时在JDK中看到很多native的方法,但是自己写JNI应用的会确很少。这次因为在进行开发中,遇到需要Java调用一个现成的成熟的功能完善的C语言应用库,因此 JNI便成了Java和C库之间的桥的功能。既然是初探,那么下面我们就从HelloWorld开始吧:虽然Java是一个跨平台的语言,但是C语言是一个和平台密切相关的语言;所以针对Unix、Linux、Windows等不同的操作系统,C语言的预编译,动态库的创建等都有一些差别,也需要我们分别进行处理之。下面仅基于Sun的Unix平台Sun Solaris开发为例。
1.首先声明第一个Java native程序:HelloWorld.java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class HelloWorld {
static {
init();
}

private native int hello();

public int sayHello() {
return hello();
}

/**
* @param args
*/

public static void main(String[] args) {
HelloWorld hw = new HelloWorld();
System.out.println("Say in Java:" + hw.sayHello());
}

private static void init() {
System.out.println("Starting to load HelloWorld lib.");
try {
System.loadLibrary("HelloWorld");
} catch (Throwable t) {
System.out.println("Load unsuccessfully.");
t.printStackTrace();
System.exit(-1);
}
System.out.println("Load successfully.");
}
}

2.编译该Java文件。

1
javac HelloWorld.java

3.生成相应的C语言的HelloWorld.h头文件,javah HelloWorld,头文件内容如下:(其中,static Java方法和非static Java方法经过javah -jni生成的C头文件是有区别的,差异在于参数,static Java方法生成的相应的C方法的第二个参数是jclass类型,非static Java方法生成的相应的C方法的第二个参数是jobject类型。)

1
more HelloWorld.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: hello
* Signature: ()I
*/

JNIEXPORT jint JNICALL Java_HelloWorld_hello
(JNIEnv *, jobject);


#ifdef __cplusplus
}
#endif
#endif
4.开始编写第一个c语言的JNI的本地(native)方法:
vi HelloWorld.c
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>

JNIEXPORT jint JNICALL Java_HelloWorld_hello
(JNIEnv* env, jobject target)
{

printf("Hello World in C program!!!\n");
return 2;
}

5.生成Unix下的动态库.so文件:

1
cc -G -I. -I$JAVA_HOME/include -I$JAVA_HOME/include/solaris HelloWorld.c -o libHelloWorld.so

在当前目录下看到新生成的libHelloWorld.so文件。也许你会注意到我们在Java程序中System.loadLibrary()加载的是库HelloWorld,那么这其实是一个命名的规范而已。以lib开头的.so文件即是Unix下的动态库,而JNI加载器通过HelloWorld这个名字实际上希望加载的动态库是libHelloWorld.so。
6.设置环境变量,查看LD_LIBRARY_PATH并设置:

1
vi .cshrc

1
2
setenv LD_LIBRARY_PATH '.:/usr/local/lib:$LD_LIBRARY_PATH'
source .cshrc

这个环境变量设置的是Java的动态库加载器需要加载的C动态库所在的路径。因此,我们生成的libHelloWorld.so一定要在这个环境变量中设置,在这个例子中我就把当前目录.加到环境变量LD_LIBRARY_PATH了:)
7.执行HelloWorld文件,java HelloWorld,在标准输出上看到:

1
2
3
4
Starting to load HelloWorld lib.
Load successfully.
Hello World in C program!!!
Say in Java:2

一切Okey,如此的easy,一阵狂喜!接下来就是将这个例子运用于JNI的开发之中了。

Resource:
《Java Native Interface: Programmer’s Guide and Specification》
http://java.sun.com/docs/books/jni/html/start.html#27008
http://linuxmafia.com/faq/Admin/ld-lib-path.html
https://www6.software.ibm.com/developerworks/cn/education/java/j-jni/tutorial/j-jni-2-15.html