java通过jni调用c入门

JNI(Java Native Interface)是java平台的一部分,它允许java和其他语言写的代码交互。本篇记录java调用c函数的过程,通过一个helloworld程序说明在不同平台下是如何实现java调用c的。

过程记录

编写java本地接口

直接上代码:

1
2
3
public class HelloJni {
public native void sayHello();
}

如果要将一个方法作为本地方法,必须声明该方法为native,且不能实现。
编译:javac HelloJni

编写本地接口的实现

通过上述操作会生成一个HelloJni.h头文件,内容就不贴上来了。下面是本地接口的c语言实现:

1
2
3
4
5
6
7
8
/*HelloJniImpl.c*/
#include <jni.h>
#include "HelloJni.h"
JNIEXPORT void JNICALL
Java_HelloJni_sayHello(JNIEnv *env, jobject obj)
{
printf("hello, world.\n");
}

生成动态库

不同系统的不同编译环境下,生成动态库的方式是不一样的,下面记录几种常用的方式。

windows下用vc生成

进入cmd执行命令:

1
> cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloJni.c -Fehello.dll

生成hello.dll动态库文件,供java本地调用。

windows下用mingw生成

进入GitBash执行命令:

1
$ gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I/c/Java/jdk/include -I/c/java/jdk/include/win32 -shared -o hello.dll HelloJni.c

其中-Wall -D_JNI_IMPLEMENTATION_不是必须的,但-Wl,--kill-at是必须的,否则生成的dll无法被java调用。

linux下生成

linux下就相对简单一些,跟普通的动态库生成没有区别,上命令:

1
$ gcc -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -fPIC -shared -o libhello.so HelloJni.c

注意linux下java根据动态库名字找的时候会拼上前缀lib,所以为了测试代码一致,直接生成libhello.so文件。

编写java测试程序

直接上代码:

1
2
3
4
5
6
7
8
9
public class HelloWorld {
static {
System.loadLibrary("hello");
}
public static void main(String[] args) {
HelloJni hj = new HelloJni();
hj.sayHello();
}
}

使用static块加载相应名称的动态库。

1
2
$ javac HelloWorld #编译
$ java HelloWorld #测试运行

我的环境是64位win7,开始用的是64位jdk,但编译动态库用的是32位mingw,这样运行测试时提示64位环境不能调用32位动态库。换成32位jdk就好了。

另外,windows下是会在程序运行目录下自动寻找动态库,但linux下不会自动寻找程序当前目录,我一般的做法是export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.,这样做是为了让程序也找当前目录。

其他

这篇文章是一个java调用c函数的入门,实际用途不大,例如调用的函数sayHello既没有返回值也没有参数,我们编写代码时一般不会写这样的函数。这就涉及到了相关数据类型的对应及转换问题。等再写一篇介绍吧。这里先贴上基本数据类型的对应关系。
java|jni|c
:—|:—|:—
boolean|jboolean|unsigned char
byte|jbyte|char
char|jchar|unsigned short
short|jshort|short
int|jint|long
long|jlong|__int64
float|jfloat|float
double|jdouble|double