JNI编程系列之基础篇
最近干一个活需要从Java调用C++编译的动态链接库,研究了一下JNI,现在将网上搜罗的文档和自己的体会贡献出来。
JNI的做法是:通过在方法前加上关键字native来识别本地方法,然后用本地语言(如C,C++)来实现该方法,并编译成动态链接库,在Java的类中调用该动态链接库,然后就可以像使用Java自己的方法一样使用native方法了。这样做的好处是既具有了Java语言的便利性,又具有了C语言的效率;另一个好处是可以利用已有的C代码,避免重复开发。
下面从最简单的JNI程序入手,介绍如何进行JNI编程。
下面是一个简单的Java程序HelloWorld.java,
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.loadLibrary("HelloWorld");
}
}
在这个例子中,注意到两个关键的地方。
首先是第二行
private native void print();
如果没有native关键字,这一行代码就是普通Java方法的声明。关键字native表明这是一个用本地语言实现的方法。
第二个地方是
System.loadLibrary("HelloWorld");
这行代码的作用是调用名为HelloWorld的动态链接库,在Windows下,是HelloWorld.dll,在Linux下是HelloWorld.so。
显然现在这个Java程序是不能运行的。要运行它先要做下面的工作。执行
> javac HelloWorld.java
> javah -jni HelloWorld
执行完这两条语句之后,会生成一个名为HelloWorld.h的文件,它的内容应该是这样的,
/* DO NOT EDIT THIS FILE - it is machinegenerated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALLJava_HelloWorld_print (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
注意到在这个程序的开头有这样一行代码,
#include <jni.h>
这里的jni.h,只要你安装了JDK就能在安装目录下找到它。
不要修改这个文件的内容,现在要做的是写一个名为HelloWorld.cpp程序,实现上面这个.h文件里的函数,
//------------
#include "HelloWorld.h"
#include <iostream>
JNIEXPORT void JNICALLJava_HelloWorld_print (JNIEnv *, jobject) {
std::cout << "Hello World!"<< std::endl;
}
//--------------
这是一个最简单的C++程序。将它编译为动态链接库,我们得到HelloWorld.dll,将这个.dll文件拷到HelloWorld.java文件所在的目录下。执行
> java HelloWorld
你会看到屏幕上输出
> Hello World!
现在来总结一下,要实现JNI编程,需要以下几个步骤:
1. 写一个Java程序,将你希望用C语言实现的方法用native关键字标识出来,同时加上调用动态链接库的语句。
System.loadLibrary("HelloWorld");
2. 执行下面两条语句,生成.h文件
> javac HelloWorld.java
在class或bin目录下(其下或其子目录下有 javac命令生成的*.class文件)执行
> javah -jni HelloWorld
3. 根据.h文件,写一个.cpp程序,编译成动态链接库,并将其复制到.java文件所在的路径下。
4. 执行java HelloWorld
这样,就学会了最简单的JNI编程,网上能google到的大部分文章也就到此为止了。但是你一定还有很多疑问,就像我刚开始一样,最容易想到的就是,如果本地方法要传递参数或者返回值怎么办?本地方法的定义在.java文件中,参数或者返回值的类型都是Java的类型。而它的实现是通过C程序完成的,参数和返回值的类型只能是C的类型。诸如此类的问题,上面这个简单的例子是回答不了的。在下一篇,我将解释这些问题。