JNA加载存在多个依赖的so动态库
之前记录过在windows上加载单个ddl动态库(JNA简单使用(一)(java和c++互操作) - 浪迹天涯的派大星 - 博客园 (cnblogs.com)),这次记录一下在linux上调用存在多个依赖的so动态库。
1、背景
需要c++分片处理一种特殊格式的文件,Java接受分片数据后保存,采用JNA的方式调用c++动态库的方式实现。
2、Java代码
注:此处Java精简了逻辑,只为简单体现JNA调用c++的方式
2.1、Library接口代码
区别之一:load动态库时,windows上ddl不用写后缀,linux上so需要写后缀
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
public interface SlicerLibrary extends Library {
SlicerLibrary LIBRARY = Native.load("libEnv.so", SlicerLibrary.class);
/**
* 初始化环境
*
* @return 是否成功
*/
boolean initSlice();
/**
* 通知c++清除缓存
*/
void freeSliceCache();
/**
* 文件分片完成,释放资源
*/
void unInitSlice();
/**
* 获取分片数据
*
* @param filePath 完整文件路径
* @param objCounts 每个分片文件包含的实体数量
* @return 总的分片个数
*/
Integer fileSlice(WString filePath, int objCounts);
/**
* 获取序列号index的分片大小
*
* @param index
* @return 分片大小(单位byte)
*/
int getSliceBufferLength(int index);
/**
* 获取分片数据的字节数组
*
* @param index 分片序号
* @param pSliceBuffer 分片数据
* @return 是否成功
*/
boolean getSliceBuffer(int index, Pointer pSliceBuffer);
}
2.2、调用动态库的Java代码
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
public class SlicerService {
/**
* 每个分片中包含的实体数量
*/
private static final int objCountsPerPackage = 100;
/**
* 静态变量,确保不会被JVM垃圾回收
*/
private static SlicerLibrary library = null;
/**
* 初始化动态库标志位
*/
private static boolean initFlag = false;
public void saveSliceFile(String filePath) {
//初始化动态库
initSliceLibrary();
//c++对应的数据类型是w_chat
WString path = new WString(filePath);
//获取分片数量
Integer count = library.fileSlice(path, objCountsPerPackage);
//循环处理所有的分片
if (count != null && count > 0) {
for (int i = 0; i < count; i++) {
//获取每个分片文件的数据长度
int size = library.getSliceBufferLength(i);
//分配内存大小
Pointer buffer = new Memory(size);
try {
if (library.getSliceBuffer(i, buffer)) {
//获取数据
byte[] data = buffer.getByteArray(0, size);
//保存数据
saveData(data);
}
} finally {
//清除分配的内存
Native.free(Pointer.nativeValue(buffer));
//避免GC时,重复调用Native.free().而导致程序异常退出
Pointer.nativeValue(buffer, 0L);
//引用置空,便于垃圾回收
buffer = null;
}
}
}
}
/**
* 初始化动态库
*/
private void initSliceLibrary() {
if (!initFlag) {
library = SlicerLibrary.LIBRARY;
synchronized (this) {
if (!initFlag) {
initFlag = library.initSlice();
}
}
}
}
/**
* 保存实际数据
*
* @param data
*/
private void saveData(byte[] data) {
//省略保存逻辑
}
}
3、需要依赖的其他动态库的存放位置
将依赖的动态库,存放在linux服务器的任意文件夹下,然后将文件夹路径,配置进LD_LABRARY_PATH。
例如:/home/test,假设Java需要加载的入口so动态库是test1.so,c++文件工程结构如下:
则应该执行:
export LD_LABRARY_PATH=$LD_LABRARY_PATH:/home/test:/home/test/folder1:/home/test/folder2
注:$LD_LABRARY_PATH的作用是防止之前已经配置的路径被覆盖,在后面增加新的路径即可,多个文件夹路径通过冒号(:)分割。
4、export和LD_LABRARY_PATH说明
4.1、LD_LABRARY_PATH
LD_LABRARY_PATH是用于指定动态链接器(ld)查找ELF可执行文件运行时所依赖的动态库(so)的路径。即用于在程序运行期间查找动态链接库时,指定除了系统默认路径(/usr/lib)之外的路径。
LD_LABRARY_PATH,无法识别文件夹里面的子文件夹,子文件夹需要单独配置
4.2、export
Linux中,export
命令用于新增、修改和删除Linux上的环境变量(对PATH、 LIBRARY_PATH、 LD_LIBRARY_PATH),只针对当前用户当前窗口登录有效,新开窗口需要重新执行;
如果要使其永久生效,则需要在linux环境变量中配置,编辑vim ~/.bashrc
,增加export语句,然后source ~/.bashrc
,使其立即生效。
参考:1、Linux中修改环境变量及生效方法(永久、临时)环境变量查看_linux 激活环境变量
2、Linux中PATH、 LIBRARY_PATH、 LD_LIBRARY_PATH的区别_ld_library_path library_path 区别