通过JNI实现Java对C/C++的调用

Posted by Elton's Blog on July 29, 2011

JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

大致步骤

  1. 编写带有native声明的方法的java类
  2. 使用javac命令编译所编写的java类
  3. 使用javah命令生成扩展名为h的头文件
  4. 使用C/C++实现本地方法
  5. 将C/C++编写的文件生成动态连接库

1) 编写java程序:

public class HelloNative{
  public native void greeting();//所有native所修饰的都是本地方法
  
  static{
    System.loadLibrary("HelloNative");//载入本地库
  }

  public static void main(String[] args){
    new HelloNative().greeting();
//    System.out.println(System.getProperty("java.library.path"));
  }
}

声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明该方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。 Load动态库:System.loadLibrary(“HelloNative”);加载动态库(我们可以这样理解:我们的方法 greeting()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“HelloNative”是动态库的名字。

2) 编译

javac HelloNative.java

3) 生成扩展名为h的头文件

javah HelloNative

命令执行后会在当前目录下生产一个c的头文件,名字为HelloNative.h。内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class HelloNative */

#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloNative
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
</pre>
这个h文件相当于我们在java里面的接口,这里声明了一个 Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致

**4) 编写本地方法实现和由javah命令生成的头文件里面声明的方法名相同的方法。**
#include 
#include 
#include "HelloNative.h"

JNIEXPORT void JNICALL Java_HelloNative_greeting
  (JNIEnv * env, jobject obj)
{
  printf("Hello, Native!\n");
}
</pre>

**5) 生成动态库**
gcc -fPIC -I/home/elton/jdk/include -I/home/elton/jdk/include/linux -shared -o libHelloNative.so HelloNative.c
注意,必须告知编译器jni.h所在的接口位置。linux的动态库都是以lib开头,以.so结尾的,要遵守这个命名规范。-fPIC是告诉编译器生成跟位置无关的动态链接库 命令执行后,会在当前目录生成一个libHelloNative.so的动态链接库文件 **6) 运行程序**
java -Djava.library.path=. HelloNative
必须指定java.library.path变量的内容,告诉java你的动态链接库的位置。 或者在命令行上输入
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
这样就不用每次调试的时候都输入-Djava.library.path=.这个参数了。 当你部署的时候,你通过System.out.println(System.getProperty("java.library.path"));得到你系统的java.library.path位置,然后把你的动态链接库拷贝到这个目录中。我用的是ubuntu 11.04 64位版本,得到的结果是
/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
这样你的动态链接库就永远都会被java访问到了,不用每次指定环境变量了。