Java本地运行时(JNR)是另一个旨在解决JNI复杂性的开源尝试。它是JNA的有力竞争者,拥有比JNI更直观和强大的API。我们可以按照以下方式将其作为依赖项添加:
<dependency> <groupId>com.github.jnr</groupId> <artifactId>jnr-ffi</artifactId> <version>2.2.13</version></dependency>
假设我们有与问题138中完全相同的C方法(sumTwoInt())和本地共享库(math.dll)。我们首先编写一个包含我们计划从Java调用并在本地代码中定义的方法和类型的声明的Java接口。我们编写包含sumTwoInt()声明的SimpleMath接口如下:
public interface SimpleMath { @IgnoreError long sumTwoInt(int x, int y);}
@IgnoreError注解指示JNR不保存errno值(https://www.geeksforgeeks.org/errno-constant-in-c/)。
接下来,我们必须指导JNR加载math.dll库并生成这个接口的具体实现,这样我们就可以调用它的的方法。为此,我们需要LibraryLoader和以下直观的代码:
public class Main { public static void main(String[] args) { LibraryLoader<SimpleMath> loader = FFIProvider.getSystemProvider() .createLibraryLoader(SimpleMath.class) .search("./jnr/cpp") .map("sumTwoInt", "_Z9sumTwoIntii"); loader = loader.map("sumTwoInt", "_Z9sumTwoIntii"); if (Platform.getNativePlatform().getOS() == Platform.OS.WINDOWS) { SimpleMath simpleMath = loader.load("math"); long result = simpleMath.sumTwoInt(3, 9); System.out.println("Result: " + result); } }}
通过LibraryLoader API,我们准备了游乐场。我们指导JNR我们的库位于jnr/cpp,通过search()方法。此外,我们通过map()方法提供方法名称的正确映射(记住从问题138中,G++通过名称混淆(或名称装饰)将方法从sumTwoInt重命名为_Z9sumTwoIntii)。最后,我们通过load()方法加载库并调用sumTwoInt()方法。
JNR提供了许多其他特性,您可以从https://github.com/jnr开始利用。您可能还对JavaCPP感兴趣,它是JNI的另一个替代品(https://github.com/bytedeco/javacpp)。