Java 本地调用(jni)编程基础
首先说明一下,根据经验来看,得益于高性能的 Java 虚拟机,使用纯 Java 实现的代码不一定就比 c 语言的慢。但是在大量的 c 语言库中还藏有不少珍宝,或许是没必要重复造轮子,或许是没有信心写出比那些经过多年考验的代码更健壮的程序,使用 Java 代码调用 C/C++ 仍然具有很高的价值。
一个 hello world 例子
本地方法的定义类似于接口函数,因为都还没实现嘛,不同的地方在于需要在返回值前面加上 native 关键字,例如下面的 Hello 类定义了 本地方法 say()
//Hello.java
public class Hello{
public native void say(String something);
}
这段程序需要 c 语言实现,首先使用 javac 工具在当前目录下生成头文件(以前有专门的 javah 工具,但是如果在新版本的java中使用,会警告说此工具已经准备被移除)
javac Hello.java -h .\
头文件内容如下
//Hello.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Hello
* Method: say
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_Hello_say
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
需要注意的是,这段代码由 javac 工具自动生成,最好不要修改任何内容。其函数命名也极具规律,遵循 Java_类名_函数名 这种格式。
然后是新建一个 Hello.c 文件实现头文件函数定义
#include <jni.h>
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Hello
* Method: say
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_Hello_say
(JNIEnv * env, jobject jobj, jstring jstr){
const char *c_str = NULL;
c_str = (*env) -> GetStringUTFChars(env, jstr, NULL);
printf(c_str);
(*env) -> ReleaseStringUTFChars(env, jstr, c_str);
return;
}
#ifdef __cplusplus
}
#endif
#endif
最后再编译并生成动态链接库
gcc -c Hello.c -I "%JAVA_HOME%\include" -I "%JAVA_HOME%\include\win32"
gcc -shared -o Hello.dll Hello.o
使用下述代码调用
//Main.java
public class Main{
public static void main(String[] args ) {
System.loadLibrary("Hello");
new Hello().say("hello jni world");
}
}