標籤:利用 直接 define fine baidu struct str cout constant
JNA定義:
JNA:java Native Access,是SUN公司開發的基於JNI的架構。JNI使得Java可以調用原生的c或者c++代碼。
JNA與JNI(Java Native Interface)的差別:
效能:JNA在效能上不如JNI。由於JNA是在JNI的基礎上封裝了一層。
移植性:JNA的可移植性要好於JNI,由於開發人員不須要再編寫作為代理的動態連結程式庫。
使用:JNI使用native關鍵字,使用一個個java方法映射原生方法,利用System.loadLibrary;JNA使用一個java借口來代表動態連結程式庫。使用Native.loadLibrary
JNA使用環境安裝:
原生代碼:使用C++或者C編寫原生代碼,或者使用已有的原生代碼。在準備在java中使用的函數或者class前註明extern “C” __declspec(dllexport)。然後打包成動態連結程式庫dll
Java代碼:下載jna.jar,https://github.com/java-native-access/jna。然後把dll檔案放在詳細的project以下。
JNA使用:
1. 準備dll(這部分針對剛開始學習的人。可直接瀏覽第2部分)
我們從產生dll到利用JNA,使用Java調用dll一步一步解說。
首先產生dll。為了學習JNA。最好我們自己動手產生dll,我是使用Dev C++(http://sourceforge.net/projects/orwelldevcpp/) 這個工具來編寫c++代碼的。安裝Dev C++之後,我們建立一個dllproject。
然後我們建立一個.h檔案以及一個.cpp檔案。我們須要在.h裡面define:
#if BUILDING_DLL#define DLLIMPORT extern "C" __declspec(dllexport)#else#define DLLIMPORT extern "C" __declspec(dllimport)#endif
然後我們在.h定義兩個Struct以及兩個函數function:
struct UserStruct{ long id; wchar_t* name; int age;};DLLIMPORTvoid sayUser(UserStruct* pUserStruct);struct CompanyStruct{ long id; wchar_t* name; UserStruct* users[100]; int count;};DLLIMPORTvoid sayCompany(CompanyStruct* pCompanyStruct);
這裡須要注意,函式宣告前須要加DLLIMPORT,這裡DLLIMPORT等於extern “C” __declspec(dllexport),Struct前不須要做特殊的處理。
在.cpp檔案裡實現sayUser以及sayCompany兩個函數:
void sayUser(UserStruct* pUserStruct){ std::wcout<<L"hello:"<<pUserStruct->name<<std::endl;}
sayUser函數的功能是把UserStruct結構體中的的name列印出來
void sayCompany(CompanyStruct* pCompanyStruct){ std::wcout<<L"hello:"<<pCompanyStruct->name<<std::endl; for(int i=0;i<pCompanyStruct->count;i++) { UserStruct* user=pCompanyStruct->users[i]; sayUser(user); }}
SayCompany函數的功能是把Company結構體中的name列印出來,同一時候把裡面全部UserStruct成員的name列印出來。
然後我們在cpp檔案裡實現
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved){ return TRUE;}
然後編譯產生dll檔案,由於我定義的project名字叫做JNATest,所以產生的dll檔案叫做JNATest.dll。
2. Java使用JNA調用dll
我們須要建立一個interface,這裡我們取名TestDll1,然後extends Library。我們須要把UserStruct和CompanyStruct兩個結構體映射到java中,代碼例如以下:
public class UserStruct extends Structure { public static class ByReference extends UserStruct implements Structure.ByReference{}; public static class ByValue extends UserStruct implements Structure.ByValue{} public NativeLong id; public WString name; public int age;}
當中ByReference指的是指標,ByValue指的是值,然後以下三個欄位分別相應.h檔案裡UserStruct中的三個欄位,當中順序一定不能錯。由於作為記憶體傳給c函數調用,讀取時依照c中結構體的順序來讀,所以順序一定不能錯。
同理CompanyStruct在java中的映射例如以下:
public class CompanyStruct extends Structure{ public static class ByReference extends CompanyStruct implements Structure.ByReference{}; public static class ByValue extends CompanyStruct implements Structure.ByValue{}; public NativeLong id; public WString name; //須要使用toArray。由於java中的記憶體空間是不連續的。所以使用JNA提供的toArray方法產生連續的記憶體空間 public UserStruct.ByReference[] users=(UserStruct.ByReference[]) new UserStruct.ByReference().toArray(100); public int count;}
這裡面有個須要特別注意的地方。否則會NULL錯誤。就是public UserStruct.ByReference[] users=(UserStruct.ByReference[]) new UserStruct.ByReference().toArray(100);這句話,假設改為public UserStruct.ByReference[] users=new UserStruct.ByReference[100],之後調用就會報錯。原因是java的記憶體空間通常是不連續的,而我們須要連續的記憶體空間,這裡須要使用JNA提供的toArray函數產生數組。
以下一段代碼非常重要就是載入dll:
TestDll1 INSTANCE=(TestDll1) Native.loadLibrary("JNATest",TestDll1.class);
然後聲明我們要在java中調用的原生函數:
public void sayUser(UserStruct userStruct);public void sayCompany(CompanyStruct companyStruct);
然後我們建立一個Test.java,然後在main函數中測試能否調用原生函數。
首先測試sayUser:
UserStruct.ByReference userStruct=new UserStruct.ByReference();userStruct.id=new NativeLong(100);userStruct.age=30;userStruct.name=new WString("SBY");TestDll1.INSTANCE.sayUser(userStruct);
然後執行。會列印出hello:SBY
再測試sayCompany:
CompanyStruct.ByReference companyStruct=new CompanyStruct.ByReference();companyStruct.id=new NativeLong(2);companyStruct.name=new WString("hehe");companyStruct.count=10;UserStruct.ByReference pUserStruct=new UserStruct.ByReference();pUserStruct.id=new NativeLong(90);pUserStruct.age=99;pUserStruct.name=new WString("sby");//pUserStruct.write();for(int i=0;i<companyStruct.count;i++){ companyStruct.users[i]=pUserStruct;}TestDll1.INSTANCE.sayCompany(companyStruct);
會輸出一個hello:hehe和10個hello:sby
有些地方會說須要pUserStruct.write()這行代碼,目的是把記憶體固定住,而不被GC釋放掉,然而在我測試的時候沒有加這一行也能準確執行。預計這個JNA提供的toArray函數有關。
以上就是我的JNA學習心得。之所以會在machine learning的部落格中插入這樣一篇,基本的目的是大多數的machine leanring的代碼都是使用c或者c++編寫。而一些情境會須要編寫java程式。此時我們須要使用java來調用已經寫好的c或者c++函數。
以上源碼傳送門:http://yun.baidu.com/share/link?
shareid=2278504517&uk=3977203577
使用JNA,讓java調用原生代碼