簡介
在JNA中,為了和native的function進行對映,我們可以有兩種mapping方式,第一種是interface mapping,第二種是direct mapping。雖然兩種方式不同,但是在具體的方法對映中,我們都需要在JAVA中定義一個和native方法進行對映的方法。
而這個JAVA中的對映在JNA中就是一個function。通過或者function物件,我們可以實現一些非常強大的功能,一起看看吧。
function的定義
先來看下JNA中Function的定義:
public class Function extends Pointer
可以看到Function實際上是一個Pointer,指向的是native function的指標。
那麼怎麼得到一個Function的例項呢?
我們知道JNA的流程是先進行Library的對映,然後再對Library中的Function進行對映。所以很自然的我們應該可以從Library中得到Function。
我們看一下根據Library name得到function例項的方法定義:
public static Function getFunction(String libraryName, String functionName, int callFlags, String encoding) {
return NativeLibrary.getInstance(libraryName).getFunction(functionName, callFlags, encoding);
}
這個方法可以接受4個引數,前面兩個引數大家應該很熟悉了,第三個引數是callFlags,表示的是函式呼叫的flags,Function定義了三個callFlags:
public static final int C_CONVENTION = 0;
public static final int ALT_CONVENTION = 0x3F;
public static final int THROW_LAST_ERROR = 0x40;
其中C_CONVENTION表示的是C語言型別的方法呼叫。
ALT_CONVENTION表示的其他的呼叫方式。
THROW_LAST_ERROR表示如果native函式的返回值是非零值的時候,將會丟擲一個LastErrorException。
最後一個引數是encoding,表示的是字串的編碼方式,實際上指的是 Java unicode和native (const char*) strings 的轉換方式。
除了根據Library name獲取Function之外,JNA還提供了根據Pointer來獲取Function的方法。
public static Function getFunction(Pointer p, int callFlags, String encoding) {
return new Function(p, callFlags, encoding);
}
這裡的Pointer指的是一個執行native方法的指標,因為Function本身就是繼承自Pointer。所以跟Pointer來建立Function的本質就是在Pointer的基礎上新增了一些Function特有的屬性。
有了Function的定義,更為重要的是如何通過Function來呼叫對應的方法。跟反射很類似,Function中也有一個invoke方法,通過呼叫invoke,我們就可以執行對應的Function的功能。
Function中的invoke方法有兩種,一種是通用的返回物件Object,一種是帶有返回值的invoke方法,比如invokeString,invokePointer,invokeInt等。
Function的實際應用
Function的實際使用和JAVA中的反射有點類似,其工作流程是首先獲得要載入的NativeLibrary,然後從該NativeLibrary中找到要呼叫的Function,最後invoke該Function的某些方法。
C語言中的printf應該是大家最熟悉的native方法了。我們看一下如何使用Function來呼叫這個方法吧:
NativeLibrary lib = NativeLibrary.getInstance(Platform.C_LIBRARY_NAME);
Function f = lib.getFunction("printf");
try {
f.invoke(getClass(), new Object[] { getName() });
fail("Invalid return types should throw an exception");
} catch(IllegalArgumentException e) {
// expected
}
可以看到呼叫的流程非常簡潔。如果是用interface Mapping或者direct Mapping的形式,我們還需要自定義一個interface或者class,並且在其中定義一個相應的java方法對映。但是如果使用Function的話,這些都不需要了。我們直接可以從NativeLibrary中拿到對應的函式,並最終呼叫其中的方法。
C語言中的printf的原型如下:
# include <stdio.h>
int printf(const char *format, ...);
printf帶有返回值的,如果要輸出這個返回值,則可以呼叫Function中的invokeInt命令。我們再來看一個有返回值的呼叫例子:
NativeLibrary lib = NativeLibrary.getInstance(Platform.C_LIBRARY_NAME);
Function f = lib.getFunction("printf");
Object[] args = new Object[Function.MAX_NARGS+1];
// Make sure we don't break 'printf'
args[0] = getName();
try {
f.invokeInt(args);
fail("Arguments should be limited to " + Function.MAX_NARGS);
} catch(UnsupportedOperationException e) {
// expected
}
總結
使用Function可以減少手寫Mapping的工作,在某些情況下是非常好用的,但是Function的invoke支援TypeMapper,並不支援FunctionMapper,所以在使用中還是有一些限制。
大家可以在使用過程中酌情考慮。
本文已收錄於 http://www.flydean.com/07-jna-function/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!