1. 程式人生 > >使用JNA,讓java呼叫原生程式碼

使用JNA,讓java呼叫原生程式碼

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檔案放在具體的工程下面。

JNA使用:

1. 準備dll(這部分針對初學者,可直接瀏覽第2部分)
我們從生成dll到利用JNA,使用Java呼叫dll一步一步講解。
首先生成dll,為了學習JNA,最好我們自己動手生成dll,我是使用Dev C++(

http://sourceforge.net/projects/orwelldevcpp/) 這個工具來編寫c++程式碼的。安裝Dev C++之後,我們建立一個dll工程。
這裡寫圖片描述
然後我們建立一個.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; }; DLLIMPORT void sayUser(UserStruct* pUserStruct); struct CompanyStruct{ long id; wchar_t* name; UserStruct* users[100]; int count; }; DLLIMPORT void 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檔案,由於我定義的工程名字叫做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

相關推薦

使用JNAjava呼叫原生程式碼

JNA定義: JNA:java Native Access,是SUN公司開發的基於JNI的框架,JNI使得Java能夠呼叫原生的c或者c++程式碼。 JNA與JNI(Java Native Interface)的區別: 效能:JNA在效能上不如JNI,

使用JNAjava調用原生代碼

hub object 重要 ive 環境 lib comm ray 執行 JNA定義: JNA:java Native Access,是SUN公司開發的基於JNI的

Java小工具Lombok安裝和使用JAVA程式碼更優雅

Lombok簡介Lombok專案通過新增“處理程式”,使java成為一種更為簡單的語言。作為一個Old Java Developer,我們都知道我們經常需要定義一系列的套路,比如定義如下的格式物件。public class DataExample { private fi

Java開發程式碼優化】lombok外掛通過java註解簡化程式碼的開發

在開發的過程中,我們如何寫出高質量的程式碼,寫出優雅的程式碼,寫出高度可擴充套件的程式碼。今天我們就簡單介紹一下如何提高我們的程式碼質量。 使用lombok簡化程式碼 在介紹lombok之前,我們先來看一段程式碼: public class Person { priva

java呼叫C++程式碼

首先我的參考部落格如下: 流程如下: 1.建立一個java檔案 public class ForDll { static { System.loadLibrary("javaCallcpp"); } public native int my

eclipse配置tomcatjava web專案執行起來! Tomcat v9.0 Could not publish to the server. java.lang.IndexOutOfBoundsException

做專案,搞開發。開發環境配置時第一步。只有環境搭好了,以後的事情才能順利進行! 這不需求來了,負一屏專案有新功能需要新增,臨時接手,要進行伺服器前端開發。這個專案是以前後臺java人員進行開發的。都是jsp頁面,那個叫心塞的啊,看的一臉不爽。但是,工作還得繼續,沒接觸過的,就得去趟趟,試試水。 把專案cl

java 呼叫介面程式碼例項

package com.zhuoshi.jcbigdata.spark.jinjingzheng; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.

這7大神器你的Python 程式碼更易於維護

  當軟體專案進入“維護模式”時,對程式碼的可讀性和編碼標準經常就忽略了(甚至從一開始就沒有建立起這些標準。)但是,在程式碼庫中保持一致的風格和測試標準是減少維護負擔的重要因素,它能確保未來的開發人員能夠快速瞭解新的專案情況 - 並保持專案的良好執行 學習Python中有不明

Java8 Nashorn實現Java呼叫javascript程式碼

1.從列印Hello World開始 Java8 引入Nashorn實現javascript呼叫,比如使用java執行一段js實現“Hello World”列印: package com.iflytek.research.jsdemo; import javax.script.ScriptEngine;

思維導圖學習 | 第二篇:java學習基礎java不再難懂【中篇】

配套Ximnd學習導圖下載地址 寫在最後 歡迎關注、喜歡、和點贊後續將推出更多的思維導圖學習文章,敬請期待。 歡迎關注我的微信公眾號獲取更多更全的學

安卓使用記錄筆記(1)ndk使用之Java呼叫c++程式碼

1.在Java中新建一個Utils類     1)指定路徑下新建一個資料夾 ,包名為Utils     2)   新建類 NativeUtils.java package com.sample.Utils; publi

java基礎思維導圖java不再難懂

思維導圖的好處 最近看了一些文章的思維導圖,發現思維導圖真是個強大的工具。瞭解了思維導圖的作用之後,覺得把它運用到java上應該是個不錯的想法,這樣回顧知識點的時候一目瞭然,快速知道自己的短板。 思維導圖不僅僅只有簡單明瞭這個好處,它還能加深人對主題

JNI java呼叫c程式碼 (一)靜態註冊

今天說的程式碼是從java層呼叫c程式碼,然後再反調java程式碼的。這裡在java用的是靜態註冊,也就是jni方法名是根據java的檔案路徑生成的,不是動態註冊。 一、java呼叫jni,靜態註冊 先看:java註冊jni,以及呼叫jni函式: package com.

java 基礎思維導圖 java 不再難懂

最近看了一些文章的思維導圖,發現思維導圖真是個強大的工具。瞭解了思維導圖的作用之後,覺得把它運用到java上應該是個不錯的想法,這樣回顧知識點的時候一目瞭然,快速知道自己的短板。 思維導圖不僅僅只有簡單明瞭這個好處,它還能加深人對主題的印象和理解,能達到快速閱讀的好處,就好像一本書的目錄大綱,當你閱讀了

思維導圖學習 | 第一篇:java學習基礎java不再難懂【上篇】

配套Ximnd學習導圖下載地址 寫在最後 歡迎關注、喜歡、和點贊後續將推出更多的思維導圖學習文章,敬請期待。 歡迎關注我的微信公眾號獲取更多更全

chromium JNI呼叫java呼叫c++程式碼

這篇blog我們主要來介紹下chromium下content目錄下的jni呼叫. JNI呼叫主要分兩塊: 1.     Java調cpp(cc) 2.     Cc(cpp)調Java 現在,我們這裡介紹Java調cc. 要實現java呼叫cc檔案,要準備三個檔案:cc檔案

使用java呼叫python程式碼

1.為什麼用java呼叫python java在web程式具有很大的優勢,然而在科學計算,機器學習等領域中不如python,使用java呼叫python就是為了能夠使得一個問題能夠更好的解決。 2.本程式環境要求 3.程式書寫 3.

簡化你的Java程式碼工作更高效|語言

計算機專家在問題求解時非常重視表示式簡潔性的價值。Unix的先驅者Ken Thompson曾經說過非常著名的一句話:“丟棄1000行程式碼的那一天是我最有成效的一天之一。”這對於任何一個需要持續支援和維護的軟體專案來說,都是一個當之無愧的目標。早期的Lisp貢獻

深入理解java列舉程式碼更簡潔、易懂提升工作效率!

以往設定常量,通常將常量在介面中,在JDK1.5版本新增列舉型別後就逐漸取代了這種常量定義方式 列舉常量定義 列舉常用方法 values():該方法可以將列舉型別成員以陣列的形式返回。 valueOf():該方法可以實現將普通字串轉換為列舉例項。 compare

SparkStreaming部分:OutPutOperator類SaveAsHadoopFile運算元(實際上底層呼叫textFileStream讀取的跟前兩種有一些區別)【Java版純程式碼

package streamingOperate.output; import java.util.Arrays; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; imp