从我过去所做的研究来看,只有两种方法可以加载原生库:修改java.library.path
和使用System.loadLibrary
(我觉得大多数人都这样做),或者使用System.load
绝对路径。
正如您所提到的,java.library.path
在配置 SBT 和 Eclipse 方面弄乱可能很烦人,而且我认为不可能自动为可执行 jar 做。
就这样离开了System.load
。在编写自己的本机库方面,您可以做的是:
- 创建一个 SBT 任务来编译您的原生源代码(使用
javah
和gcc
),获取生成的 .so 文件和它所依赖的任何 .so 文件,将它们放入目标目录中的 jar(作为资源)中,并将路径添加到 jar到unmanagedJars in Compile
.
- 创建一个 Scala 方法来加载库。代替调用
System.loadLibrary
,它将Class.getResourceAsStream
用于读取库,File.createTempFile
将其写入文件系统的某个位置,并将System.load
其加载到 JVM 中。
- 现在不再
System.loadLibrary
像以前那样调用,而是调用MyClasspathJniLoader.loadLibrary
.
这将适用于 SBT 运行、Eclipse 和可执行 jar,无需任何额外配置(尽管我不知道 proguard 如何知道要包含哪些资源)。
现在对于已经编写好的第三方原生库,其中一些像jblas已经使用了这种“fat jar”方法。如果他们希望您进行设置java.library.path
,然后System.loadLibrary
在他们喜欢的时候打电话给他们,那么您需要做一些魔术来完成这项工作。
我没有尝试过,但这个解决方案可能有效:
- 使用类似的 SBT 任务将其本机路径上的所有库作为资源放入 jar 中,并将该 jar 放在 clsaspath 中。
- 创建一个 Scala 方法,该方法接受一个函数和一个库名称列表,创建一个临时目录,将这些库从 jar 的资源中读取到临时目录中的文件中,将临时目录添加到
java.library.path
,调用传入的函数,最后恢复java.library.path
回到以前的样子。
- 当您第一次调用本机库时(当它可能会静态初始化并进行
System.loadLibrary
调用时),将该特定调用与它将加载的库列表一起包装在您的方法中。这样,当它调用时,它System.loadLibrary
的所有库都将打开java.library.path
并成功加载。
显然这很烦人,因为您必须在使用第三方库之前手动初始化它,但是包装所有初始化点(您的主要功能和测试初始化)似乎比让所有工具java.library.path
正确设置更可行. 如果您已经在第三方库之上拥有自己的抽象层,这可能会比这更容易,因此您实际上只需要包装一个初始化点。
如果这看起来是一个现实的解决方案,如果您感到困惑,我可以添加有关 SBT 任务或 Scala 包装器方法的更多详细信息。