Java调用本地代码(一)—— 创建动态链接库
近期项目需要使用Java调用本地一个很老的C写的库,就顺便研究了下Java调用本地代码的一些方法。整理一下出一个小系列。主要就是介绍两种不同的方法:JNI和JNA。作为第一篇,我们先动手用C写个简单的动态链接库,为后面的文章做准备。
什么是动态链接库
先做个基础的普及。C/C++编译出来的库有两种:静态链接库和动态链接库。
静态库后缀名在Windows上是.lib
,Unix/Linux上是.a
。当你的程序在编译时引用静态库,编译器会将整个静态库都包含在你编译后的可执行文件中,所以可执行文件会很大,但是程序执行时就不再需要静态库了。
动态库后缀名在Windows上是.dll
,一般存放在C:\Windows\System32
下;Unix/Linux上是.so
,一般存放在/lib
或/usr/lib
下。程序编译时,编译器不会将动态库包含在生成的可执行文件中,所以引用动态库的可执行文件较小,程序会在运行过程中动态加载所需要的库。对于Java程序员就可以将它简单的想象成.jar
文件。在Unix/Linux上,动态链接库一般都命名为”libxxx.so”,其中”xxx”是库名。
创建动态链接库
这里我们使用Linux环境,用C语言创建一个动态链接库”libhello.so”,库里面就提供一个hello()
函数。根据输入的字符串,在屏幕上打印相应的欢迎信息。
- 编写头文件”hello.h”,声明
hello()
函数
#ifndef _HELLO_H_
#define _HELLO_H_
/* C++需加上extern "C" 声明 */
#ifdef __cplusplus
extern "C" {
#endif
void hello(const char *);
#ifdef __cplusplus
}
#endif
#endif
- 编写程序文件”hello.c”,根据输入参数打印Hello信息
#include "hello.h"
#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
编译生成动态链接库
$ gcc hello.c -fPIC -shared -o libhello.so
现在你就可以在当前目录下找到”libhello.so”文件。这样,动态链接库就创建好了。我们先写个C程序测试下。
测试动态链接库
我们写个C程序来调用刚才创建的动态链接库。
- 编写测试程序”test.c”
#include "hello.h"
void main(int argc, char *argv[])
{
if (argc > 1)
{
hello(argv[1]);
}
else
{
hello("World");
}
}
编译并生成可执行文件
$ gcc test.c -L . -lhello -o test
参数”-L .“表明除了系统指定的目录外,还会从当前目录里搜索需要引用的库。参数”-lhello”指明需要链接名为hello的库,也就是文件名为”libhello.so”的库。编译后,会在本地生成名为”test”的可执行文件。
查看可执行文件的依赖库
Linux提供”ldd”命令来查看可执行文件运行时需依赖的动态链接库。我们查看下刚才生成的”test”文件:$ ldd ./test
命令执行后,你会看到类似于下面的这段信息
linux-vdso.so.1 => (0x00007fffba3fe000) libhello.so => ./libhello.so (0x00007f20a996b000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f20a958e000) /lib64/ld-linux-x86-64.so.2 (0x00007f20a9b6f000)
从第二行中你可以看到,该程序依赖于库”libhello.so”,而且该库文件存在于当前目录下
./libhello.so
。如果你将该文件移走,这里就会显示”not found”,程序自然也无法执行。运行可执行文件
在运行前,你先要将当前目录加到环境变量LD_LIBRARY_PATH
中。程序执行时,会在其中搜索需加载的库。$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
运行测试程序
$ ./test Billy
你可以看到屏幕上打印出了
Hello Billy!
恭喜你,测试通过!
用C++也一样,这里就不再做描述了。下一篇,我们将介绍如何使用JNI的方法从Java语言调用刚才创建的动态链接库。
本例代码可以从这里下载