java高級用法之JNA中的回調問題_第1頁
java高級用法之JNA中的回調問題_第2頁
java高級用法之JNA中的回調問題_第3頁
java高級用法之JNA中的回調問題_第4頁
java高級用法之JNA中的回調問題_第5頁
已閱讀5頁,還剩5頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第java高級用法之JNA中的回調問題目錄簡介JNA中的Callbackcallback的應用callback的定義callback的獲取和應用在多線程環境中使用callback總結

簡介

什么是callback呢?簡單點說callback就是回調通知,當我們需要在某個方法完成之后,或者某個事件觸發之后,來通知進行某些特定的任務就需要用到callback了。

最有可能看到callback的語言就是javascript了,基本上在javascript中,callback無處不在。為了解決callback導致的回調地獄的問題,ES6中特意引入了promise來解決這個問題。

為了方便和native方法進行交互,JNA中同樣提供了Callback用來進行回調。JNA中回調的本質是一個指向native函數的指針,通過這個指針可以調用native函數中的方法,一起來看看吧。

JNA中的Callback

先看下JNA中Callback的定義:

publicinterfaceCallback{

interfaceUncaughtExceptionHandler{

voiduncaughtException(Callbackc,Throwablee);

StringMETHOD_NAME="callback";

ListStringFORBIDDEN_NAMES=Collections.unmodifiableList(

Arrays.asList("hashCode","equals","toString"));

}

所有的Callback方法都需要實現這個Callback接口。Callback接口很簡單,里面定義了一個interface和兩個屬性。

先來看這個interface,interface名字叫做UncaughtExceptionHandler,里面有一個uncaughtException方法。這個interface主要用于處理JAVA的callback代碼中沒有捕獲的異常。

注意,在uncaughtException方法中,不能拋出異常,任何從這個方法拋出的異常都會被忽略。

METHOD_NAME這個字段指定了Callback要調用的方法。

如果Callback類中只定義了一個public的方法,那么默認callback方法就是這個方法。如果Callback類中定義了多個public方法,那么會選擇METHOD_NAME=callback的這個方法作為callback。

最后一個屬性就是FORBIDDEN_NAMES。表示在這個列表里面的名字是不能作為callback方法使用的。

目前看來是有三個方法名不能夠被使用,分別是:hashCode,equals,toString。

Callback還有一個同胞兄弟叫做DLLCallback,我們來看下DLLCallback的定義:

publicinterfaceDLLCallbackextendsCallback{

@java.lang.annotation.Native

intDLL_FPTRS=16;

}

DLLCallback主要是用在WindowsAPI的訪問中。

對于callback對象來說,需要我們自行負責對callback對象的釋放工作。如果native代碼嘗試訪問一個被回收的callback,那么有可能會導致VM崩潰。

callback的應用

callback的定義

因為JNA中的callback實際上映射的是native中指向函數的指針。首先看一下在struct中定義的函數指針:

struct_functions{

int(*open)(constchar*,int);

int(*close)(int);

};

在這個結構體中,定義了兩個函數指針,分別帶兩個參數和一個參數。

對應的JNA的callback定義如下:

publicclassFunctionsextendsStructure{

publicstaticinterfaceOpenFuncextendsCallback{

intinvoke(Stringname,intoptions);

publicstaticinterfaceCloseFuncextendsCallback{

intinvoke(intfd);

publicOpenFuncopen;

publicCloseFuncclose;

}

我們在Structure里面定義兩個接口繼承自Callback,對應的接口中定義了相應的invoke方法。

然后看一下具體的調用方式:

Functionsfuncs=newFunctions();

lib.init(funcs);

intfd=funcs.open.invoke("myfile",0);

funcs.close.invoke(fd);

另外Callback還可以作為函數的返回值,如下所示:

typedefvoid(*sig_t)(int);

sig_tsignal(intsignal,sig_tsigfunc);

對于這種單獨存在的函數指針,我們需要自定義一個Library,并在其中定義對應的Callback,如下所示:

publicinterfaceCLibraryextendsLibrary{

publicinterfaceSignalFunctionextendsCallback{

voidinvoke(intsignal);

SignalFunctionsignal(intsignal,SignalFunctionfunc);

}

callback的獲取和應用

如果callback是定義在Structure中的,那么可以在Structure進行初始化的時候自動實例化,然后只需要從Structure中訪問對應的屬性即可。

如果callback定義是在一個普通的Library中的話,如下所示:

publicstaticinterfaceTestLibraryextendsLibrary{

interfaceVoidCallbackextendsCallback{

voidcallback();

interfaceByteCallbackextendsCallback{

bytecallback(bytearg,bytearg2);

voidcallVoidCallback(VoidCallbackc);

bytecallInt8Callback(ByteCallbackc,bytearg,bytearg2);

}

上例中,我們在一個Library中定義了兩個callback,一個是無返回值的callback,一個是返回byte的callback。

JNA提供了一個簡單的工具類來幫助我們獲取Callback,這個工具類就是CallbackReference,對應的方法是CallbackReference.getCallback,如下所示:

Pointerp=newPointer("MultiplyMappedCallback".hashCode());

CallbackcbV1=CallbackReference.getCallback(TestLibrary.VoidCallback.class,p);

CallbackcbB1=CallbackReference.getCallback(TestLibrary.ByteCallback.class,p);

("cbV1:{}",cbV1);

("cbB1:{}",cbB1);

輸出結果如下:

INFOcom.flydean.CallbackUsage-cbV1:Proxyinterfacetonativefunction@0xffffffffc46eeefc(com.flydean.CallbackUsage$TestLibrary$VoidCallback)

INFOcom.flydean.CallbackUsage-cbB1:Proxyinterfacetonativefunction@0xffffffffc46eeefc(com.flydean.CallbackUsage$TestLibrary$ByteCallback)

可以看出,這兩個Callback實際上是對native方法的代理。如果詳細看getCallback的實現邏輯:

privatestaticCallbackgetCallback(Classtype,Pointerp,booleandirect){

if(p==null){

returnnull;

if(!type.isInterface())

thrownewIllegalArgumentException("Callbacktypemustbeaninterface");

MapCallback,CallbackReferencemap=directdirectCallbackMap:callbackMap;

synchronized(pointerCallbackMap){

ReferenceCallback[]array=pointerCallbackMap.get(p);

Callbackcb=getTypeAssignableCallback(type,array);

if(cb!=null){

returncb;

cb=createCallback(type,p);

pointerCallbackMap.put(p,addCallbackToArray(cb,array));

//NoCallbackReferenceforthiscallback

map.remove(cb);

returncb;

}

可以看到它的實現邏輯是首先判斷type是否是interface,如果不是interface則會報錯。然后判斷是否是directmapping。實際上當前JNA的實現都是interfacemapping,所以接下來的邏輯就是從pointerCallbackMap中獲取函數指針對應的callback。然后按照傳入的類型來查找具體的Callback。

如果沒有查找到,則創建一個新的callback,最后將這個新創建的存入pointerCallbackMap中。

大家要注意,這里有一個關鍵的參數叫做Pointer,實際使用的時候,需要傳入指向真實naitve函數的指針。上面的例子中,為了簡便起見,我們是自定義了一個Pointer,這個Pointer并沒有太大的實際意義。

如果真的要想在JNA中調用在TestLibrary中創建的兩個call方法:callVoidCallback和callInt8Callback,首先需要加載對應的Library:

TestLibrarylib=Native.load("testlib",TestLibrary.class);

然后分別創建TestLibrary.VoidCallback和TestLibrary.ByteCallback的實例如下,首先看一下VoidCallback:

finalboolean[]voidCalled={false};

TestLibrary.VoidCallbackcb1=newTestLibrary.VoidCallback(){

@Override

publicvoidcallback(){

voidCalled[0]=true;

lib.callVoidCallback(cb1);

assertTrue("Callbacknotcalled",voidCalled[0]);

這里我們在callback中將voidCalled的值回寫為true表示已經調用了callback方法。

再看看帶返回值的ByteCallback:

finalboolean[]int8Called={false};

finalbyte[]cbArgs={0,0};

TestLibrary.ByteCallbackcb2=newTestLibrary.ByteCallback(){

@Override

publicbytecallback(bytearg,bytearg2){

int8Called[0]=true;

cbArgs[0]=arg;

cbArgs[1]=arg2;

return(byte)(arg+arg2);

finalbyteMAGIC=0x11;

bytevalue=lib.callInt8Callback(cb2,MAGIC,(byte)(MAGIC*2));

我們直接在callback方法中返回要返回的byte值即可。

在多線程環境中使用callback

默認情況下,callback方法是在當前的線程中執行的。如果希望callback方法是在另外的線程中執行,則可以創建一個CallbackThreadInitializer,指定daemon,detach,name,和threadGroup屬性:

finalStringtname="VoidCallbackThreaded";

ThreadGrouptestGroup=newThreadGroup("ThreadgroupforcallVoidCallbackThreaded");

CallbackThreadInitializerinit=newCallbackThreadInitializer(true,false,tname,testGroup);

然后創建callback的實例:

TestLibrary.Vo

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論