先陈述一下背景,最近有个Java项目,需要调用so库,在网上搜了搜,可以使用JNA这个库,调用过程也非常简单,正常代码中的加载库过程如下:
Clibrary INSTANTCE = (Clibrary) Native.synchronizedLibrary(
(Clibrary) Native.loadLibrary(
Clibrary.class.getResource("/libSvApi.so")
.getPath()
.substring(beginIndex)
, Clibrary.class
)
);
注:libSvApi.so放在resource或者lib文件夹内
以上的写法如果是Java程序直接运行是没有任何问题的,但是如果打成Jar包后,就无法加载so。然后尝试了很多种获取so路径的方法都无法正确找到so的路径。
打Jar包方法:
我用的是Eclipse传统的打Jar包方法,Export-Runnable JAR file,然后Library handling选项选择第一项,这样打完jar包so就在jar包的根目录下存放。
解决办法:
最后看了这篇文章(https://www.jianshu.com/p/84518037720c)有了启发。将文件转换成流,重新复制一份到指定位置,再加载复制的文件即可。
public boolean copyLibSo() {
InputStream input = TestClass.class.getResourceAsStream("/libSvApi.so");
File temp;
try {
temp = File.createTempFile("test", ".so");
FileOutputStream fos = new FileOutputStream(temp);
IoUtil.copy(input, fos, IoUtil.DEFAULT_BUFFER_SIZE);
GpioHelper.soPath = temp.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
上面这块代码还遇到了一个坑需要说明一下,在复制文件的时候用了一个第三方库的方法IoUtil.copy,本来传统的方式就可以完成复制过程,但是使用TestClass.class.getClassLoader().getResourceAsStream时,获取到的inputStream的available()方法在windows下获取流长度正常,但是在linux下获取长度为0,导致复制失败。所以使用了第三方库来复制,不知道第三方库内部做了什么处理。以上操作后so就被完美的复制到了系统的/tmp目录下,使用时直接加载tmp目录下的so即可,调用代码如下。
Clibrary INSTANTCE = (Clibrary) Native.synchronizedLibrary(
(Clibrary) Native.loadLibrary(
GpioHelper.soPath, Clibrary.class)
注:tmp目录重新开机后会自动清空,也可so用完后手动删除。