当前位置: 首页 > 后端技术 > Java

Java高级用法:将Native方法映射到JNA中的JAVA代码

时间:2023-04-02 09:50:47 Java

介绍无论是JNI还是JNA,最终调用的都是native方法,但是对于JAVA程序来说,必须有一个调用native方法的入口,即也就是说,我们需要在JAVA方法中定义需要调用的native方法。对于JNI,我们可以使用native关键字来定义本地方法。那么在JNA中JAVA代码中定义native方法有哪些方式呢?如果LibraryMapping要调用本地native方法,首先要做的就是加载nativelib文件。我们称这个过程为LibraryMapping,也就是将原生库映射到java代码。JNA中库映射有两种方式,接口映射和直接映射。我们先看看接口映射。如果我们要加载C库,如果我们使用接口映射的方式,我们需要创建一个接口继承库:publicinterfaceCLibraryextendsLibrary{CLibraryINSTANCE=(CLibrary)Native.load("c",CLibrary.class);}上面代码中,Library是一个接口,所有的接口映射都需要继承这个Library。然后在界面里面,通过Native.load方法加载要使用的c库。上面代码中,load方法传入了两个参数,第一个参数是库的名称,第二个参数是interfaceClass。下表显示了LibraryName和传递的名称之间的映射关系:OSLibraryNameStringWindowsuser32。dlluser32LinuxlibX11.soX11MacOSXlibm.dylibmMacOSXFramework/System/Library/Frameworks/Carbon.framework/CarbonCarbonAnyPlatformcurrentprocessnull其实load也可以接受options的一个Map参数。默认情况下,JAVA接口中要调用的方法名是本地库中定义的方法名,但在某些情况下我们可能需要在JAVA代码中使用不同的名称。此时可以传入第三个参数map,map的key可以是OPTION_FUNCTION_MAPPER,value是一个FunctionMapper,用于将JAVA中的方法名映射到native库。传入的每个本机库都可以由NativeLibrary的实例表示。也可以通过调用NativeLibrary.getInstance(String)获取此NativeLibrary的实例。加载本机库的另一种方法是直接映射。直接映射使用在static块中调用Native.register的方法来加载native库,如下所示:publicclassCLibrary{static{Native.register("c");}}函数映射我们加载原生库后,下一步就是定义需要调用的函数。其实就是JAVA代码到nativelib中函数的映射,我们称之为FunctionMapping。和LibraryMapping一样,FunctionMapping也有两种方法。分别是接口映射和直接映射。在接口映射中,我们只需要根据原生库中的方法名定义相同的方法即可。这个方法不需要实现,也不需要像JNI那样用native修饰,如下所示:不必与本机库中的方法名称相同。您可以通过将FunctionMapper传递给Native.load方法来实现此目的。或者,您可以通过向方法添加native修饰符来使用直接映射:publicclassHelloWorld{publicstaticnativedoublecos(doublex);publicstaticnativedoublesin(doublex);static{Native.register(平台.C_LIBRARY_NAME);}publicstaticvoidmain(String[]args){System.out.println("cos(0)="+cos(0));System.out.println("sin(0)="+sin(0));}}对于直接映射,JAVA方法可以映射到本地库中的任何静态或对象方法。直接映射虽然有点类似于我们常用的javaJNI,但是直接映射也有一些局限性。在大多数情况下,直接映射和接口映射具有相同的映射类型,但不支持将Pointer/Structure/String/WString/NativeMapped数组作为函数参数值。在使用TypeMapper或NativeMapped的情况下,直接映射不支持NIOBuffers或基本类型的数组作为返回值。如果要使用基本类型的包装类,就必须使用自定义的TypeMapper。JAVA中的对象方法映射,映射最终会创建一个Function对象。InvocationMapping说完了库映射和函数映射,接下来我们来解释一下InvocationMapping。InvocationMapping代表Library中的OPTION_INVOCATION_MAPPER,其对应的值为一个InvocationMapper。前面我们提到了FunctionMapper,它可以实现JAVA中定义的方法名与nativelib中的方法名不同,但不能修改方法调用的状态或过程。InvocationMapper更进一步,允许您任意重新配置函数调用,包括更改方法名称以及重新排序、添加或删除参数。这是一个例子:newInvocationMapper(){publicInvocationHandlergetInvocationHandler(NativeLibrarylib,Methodm){if(m.getName().equals("stat")){finalFunctionf=lib.getFunction("_xstat");returnnewInvocationHandler(){publicObjectinvoke(Objectproxy,Methodmethod,Object[]args){Object[]newArgs=newObject[args.length+1];System.arraycopy(args,0,newArgs,1,args.length);newArgs[0]=Integer.valueOf(3);//_xstat版本返回f.invoke(newArgs);}};}返回空值;我们在InvocationMapper中实现了getInvocationHandler方法,根据给定的JAVA代码中的方法查找具体的nativelib,然后获取lib中的函数,最后调用函数的invoke方法来实现方法的最终调用。在这个过程中,我们可以修改当事人传入的参数,或者为所欲为。另一种情况是c语言中的inline函数或预处理宏,如下所示://原始C代码(宏和inline变体)#defineallocblock(x)malloc(x*1024)staticinlinevoid*allocblock(size_tx){返回malloc(x*1024);}上面的代码定义了一个allocblock(x)宏,实际上等于malloc(x*1024)。在这种情况下,您可以使用InvocationMapper将allocblock与特定的malloc一起使用来替换://InvocationmappingnewInvocationMapper(){publicInvocationHandlergetInvocationHandler(NativeLibrarylib,Methodm){if(m.getName().equals("allocblock")){finalFunctionf=lib.getFunction("malloc");returnnewInvocationHandler(){publicObjectinvoke(Objectproxy,Methodmethod,Object[]args){args[0]=((Integer)args[0]).intValue()*1024;返回f.invoke(newArgs);}};}返回空值;}}防止VM崩溃JAVA方法和native方法映射肯定会出现一些问题,如果映射方法不对或者参数不匹配,很可能会出现内存访问e错误,并可能导致VM崩溃。通过调用Native.setProtected(true),可以将VM崩溃转化为对应的JAVA异常。当然,并非所有平台都支持保护。如果平台不支持保护,则Native.isProtected()将返回false。如果要使用保护,还应该同时使用jsig库,防止信号与JVM信号冲突。libjsig.so一般存放在JRE的lib目录下,${java.home}/lib/${os.arch}/libjsig.so,设置环境变量为LD_PRELOAD(或LD_PRELOAD_64)即可使用。性能方面的考虑上面我们提到了JNA的两种映射方式,即接口映射和直接映射。相比之下,直接映射更高效,因为直接映射调用本地方法更高效。但是我们上面也提到了直接映射在使用上有一定的局限性,所以我们在使用的时候需要做一个取舍。另外,我们要避免使用基本类型的封装类,因为对于native方法来说,只有基本类型匹配,如果要使用封装类,就必须使用Type映射,造成性能损失。综上所述,JNA是调用本地方法的强大工具。若是掌握了数量,绝对会如虎添翼。本文已收录于http://www.flydean.com/03-jna-library-mapping/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等着你等你发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!