使用JNA,讓java調用原生代碼

來源:互聯網
上載者:User

標籤:利用   直接   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調用原生代碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.