1. 程式人生 > >使用JNA呼叫c/c++的so動態庫函式

使用JNA呼叫c/c++的so動態庫函式

       最近專案收到個需求,需要呼叫c寫的函式,給的是so檔案,查閱了資料,so檔案為linux下的動態庫函式檔案,windos下為dll檔案。傳統方案用JNI方式進行連線,大致看了下JNI方式實在麻煩,崩潰中找到JNA,併成功實現了呼叫,特此記錄使用過程。

一、將so檔案放在linux伺服器下,並且指定export LD_LIBRARY_PATH路徑為你的so檔案放置路徑(so檔案需要字首lib,因為jna為去動態庫中匹配lib字首的so檔案,如libcompress.so

我這邊放置在/etc/profile

vim /etc/profile然後設定

export LD_LIBRARY_PATH=你的so檔案放置路徑

編寫好後執行:source /etc/profile使之生效 

二、maven匯入jna包

<!-- https://mvnrepository.com/artifact/com.sun.jna/jna -->
    <dependency>
      <groupId>com.sun.jna</groupId>
      <artifactId>jna</artifactId>
      <version>3.0.9</version>
    </dependency>

三、根據c提供的標頭檔案看需要呼叫的方法和其引數,在java下建立

c中格式如下:



#define uint8_t     unsigned char
#define uint16_t    unsigned short



#define COMPRESS_IR_MAX_DIF_VAL     (255) //the max different value of the ir data
#define COMPRESS_INDEX_LEN_3        (3)
#define COMPRESS_INDEX_LEN_4        (4)
#define COMPRESS_INDEX_LEN_5        (5)
#define COMPRESS_INDEX_LEN_6        (6)
#define COMPRESS_INDEX_LEN_7        (7)
#define COMPRESS_INDEX_LEN_8        (8)

#define COMPRESS_VAGUE_MAX_VALUE    	(20)//樣品資料最大模糊值
#define COMPRESS_SRC_DATA_DIVIDE_VAL	(5)//原資料整除值


typedef struct
{
    uint16_t    freq;            //frequency
    int     *srcData;       //sourdata pointer
    uint16_t    srcSize;        //source data size
    uint16_t     *diffData;      //different value buffer pointer
    uint8_t     diffSize;      //different value count
    uint8_t     *indexBuf;      //index buffer pointer
    uint16_t    indexBufSize;   //index buffer Size
    uint16_t    indexSize;     //size of the index list
    uint8_t     indexLen;       //the length of the single index value
}COMPRESS_INFO;


typedef struct
{
    int     freq;           //frequency
    int     srcData[2000];      //source data
    uint16_t srcSize;       //source data Size
}COMPRESS_INPUT;

typedef struct
{
    int			compressDataSize;
    uint8_t     compressData[2000]; //compress data pointer
}COMPRESS_OUTPUT;
typedef struct
{
    uint8_t data2:2;
    uint8_t data1:3;
    uint8_t data0:3;

    uint8_t data6:1;
    uint8_t data5:3;
    uint8_t data4:3;
    uint8_t data3:1;

    uint8_t data9:3;
    uint8_t data8:3;
    uint8_t data7:2;
}UNIT_3;
typedef struct
{
    uint8_t data0:4;
    uint8_t data1:4;
}UNIT_4;

typedef struct
{
    uint8_t data1:3;
    uint8_t data0:5;

    uint8_t data4:1;
    uint8_t data3:5;
    uint8_t data2:2;

    uint8_t data6:4;
    uint8_t data5:4;

    uint8_t data9:2;
    uint8_t data8:5;
    uint8_t data7:1;

    uint8_t data11:5;
    uint8_t data10:3;
}UNIT_5;
typedef struct
{
    uint8_t data1:2;
    uint8_t data0:6;

    uint8_t data3:4;
    uint8_t data2:4;

    uint8_t data5:6;
    uint8_t data4:2;
}UNIT_6;
typedef struct
{
    uint8_t data1:1;
    uint8_t data0:7;

    uint8_t data3:2;
    uint8_t data2:6;

    uint8_t data5:3;
    uint8_t data4:5;

    uint8_t data7:4;
    uint8_t data6:4;

    uint8_t data9:5;
    uint8_t data8:3;

    uint8_t data11:6;
    uint8_t data10:2;

    uint8_t data13:7;
    uint8_t data12:1;
}UNIT_7;
COMPRESS_OUTPUT * compress_handle(COMPRESS_INPUT *inputData);

由上可知,c中提供的為一個簡單壓縮方法,引數為COMPRESS_INPUT,返回值為COMPRESS_OUT兩個物件除了有基本資料型別,還有byte型別和int型別指標。如果引數或者返回值都只是基本型別,很簡單,這邊不講,這種引數返回值都為物件,並且屬性涉及指標(java沒有指標的概念),稍微複雜一些,廢話不多說,直接上程式碼,首先建立入參物件:

package com.intre.kksdk.bean;

import com.sun.jna.Structure;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by YQ11053 on 2018/8/28 0028.
 */
public class COMPRESS_INPUT extends Structure implements Serializable {

    public int freq;
    public int[] srcData = new int[2000];
    public int srcSize;

    public COMPRESS_INPUT() {
        allocateMemory();
    }

    public static class ByReference extends COMPRESS_INPUT implements Structure.ByReference {}
    public static class ByValue extends COMPRESS_INPUT implements Structure.ByValue {}

    @Override
    protected List<String> getFieldOrder() {
        List<String> Field = new ArrayList<String>();
        Field.add("freq");
        Field.add("srcData");
        Field.add("srcSize");
        return Field;
    }

    public int getFreq() {
        return freq;
    }

    public void setFreq(int freq) {
        this.freq = freq;
    }

    public int[] getSrcData() {
        return srcData;
    }

    public void setSrcData(int[] srcData) {
        this.srcData = srcData;
    }

    public int getSrcSize() {
        return srcSize;
    }

    public void setSrcSize(int srcSize) {
        this.srcSize = srcSize;
    }


}

上面需要注意點如下:

1、類需要繼承Structure    //這樣才能接受c中的結構體

2、用int型別的陣列去對應c中int型別的指標,並且直接初始化並設定大小

3、建構函式中執行allocateMemory();,允許計算大小

4、建立(一般都寫上)

public static class ByReference extends COMPRESS_INPUT implements Structure.ByReference {}  //引數為物件需要這麼寫
public static class ByValue extends COMPRESS_INPUT implements Structure.ByValue {}  //為基本型別這麼寫

5、重寫方法getFieldOrder,實現c的結構體和java的物件屬性值的傳遞

@Override
protected List<String> getFieldOrder() {
    List<String> Field = new ArrayList<String>();
    Field.add("freq");
    Field.add("srcData");
    Field.add("srcSize");
    return Field;
}

返回值物件同上:

package com.intre.kksdk.bean;

import com.sun.jna.Structure;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by YQ11053 on 2018/8/28 0028.
 */
public class COMPRESS_OUTPUT extends Structure implements Serializable {

    public int compressDataSize;
    public byte[] compressData = new byte[2000];

    public COMPRESS_OUTPUT(){
        allocateMemory();
    }

    public static class ByReference extends COMPRESS_OUTPUT implements Structure.ByReference {}
    public static class ByValue extends COMPRESS_OUTPUT implements Structure.ByValue {}

    @Override
    protected List<String> getFieldOrder() {
        List<String> Field = new ArrayList<String>();
        Field.add("compressDataSize");
        Field.add("compressData");

        return Field;
    }

    @Override
    public String toString() {
        return "CompressOutput{" +
                "compressData=" + Arrays.toString(compressData) +
                ", compressDataSize=" + compressDataSize +
                '}';
    }
}

實現類:

package com.intre.kksdk.utils;

import com.intre.kksdk.bean.COMPRESS_INPUT;
import com.intre.kksdk.bean.COMPRESS_OUTPUT;
import com.intre.kksdk.bean.DECOMPRESS_INPUT;
import com.intre.kksdk.bean.DECOMPRESS_OUTPUT;
import com.sun.jna.Library;
import com.sun.jna.Native;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by YQ11053 on 2018/8/28 0028.
 */
public class CompressJNA {

    private  Logger logger = LoggerFactory.getLogger(CompressJNA.class);


    public interface CompressLib extends Library {
        CompressLib INSTANCE = (CompressLib) Native.loadLibrary("compress",CompressLib.class);
        COMPRESS_OUTPUT.ByReference compress_handle(COMPRESS_INPUT compress_input); //壓縮方法
        
    }

    public  COMPRESS_OUTPUT.ByReference compress_handle(COMPRESS_INPUT compress_input){
        return CompressLib.INSTANCE.compress_handle(compress_input);
    }

    public COMPRESS_OUTPUT.ByReference compress(COMPRESS_INPUT compress_input){
        COMPRESS_OUTPUT.ByReference compressOutput = new CompressJNA().compress_handle(compress_input);
        return compressOutput;
    }

   

}

需要注意的是載入so檔案方法中,CompressLib INSTANCE = (CompressLib) Native.loadLibrary("compress",CompressLib.class);

第一個引數為so檔案出去lib字首後的名稱.

四、呼叫(windos下無法測試,需要在linux下才能執行成功,windos下可用dll檔案)

 public static void main(String[]args){
        int frec = 3600;
        COMPRESS_INPUT inputData = new COMPRESS_INPUT();
        inputData.setFreq(frec);
        int[] srcData= {2,1,4,5,6,7,8,9};
        int srcDataSize = 8;
        inputData.setSrcData(srcData);
        inputData.setSrcSize(srcDataSize);
        COMPRESS_OUTPUT compressOutput = new CompressJNA().compress(inputData); //呼叫so函式獲取壓縮物件
    }

ps:伺服器由於某些原因關閉重啟後需要重新執行source /etc/profile  然後再啟動tomcat使之生效。 

相關推薦

使用JNA呼叫c/c++的so動態函式

       最近專案收到個需求,需要呼叫c寫的函式,給的是so檔案,查閱了資料,so檔案為linux下的動態庫函式檔案,windos下為dll檔案。傳統方案用JNI方式進行連線,大致看了下JNI方式實在麻煩,崩潰中找到JNA,併成功實現了呼叫,特此記錄使用過程。 一、將s

c++呼叫c++的so動態2

1.環境  ubuntu 14.04  g++ 4.8.4 2.有類的情況 1)庫檔案 a)原始碼 //cppl2.h class cal { public: cal(); virtual ~

Java呼叫C/C++生成的動態函式

問題背景 之前的文章中,筆者將超長整數的四則運算利用C語言實現,因個人需要在web專案中使用該功能, 此時能想到的辦法是重寫實現過程,即利用Java重寫一遍C的實現過程 不談工作量的多少,單單是這個重寫的過程就讓我望而生畏,程式設計師最頭疼的一個是bug找不到,還有一個就是

C++批量載入動態函式方法

1、列舉定義enum  {    // 0 - GigE DLL (implicitly called)    Func_isVersionCompliantDLL,    Func_isDriverAv

程式碼告訴你:Java到底是怎麼呼叫dll&so動態的?

使用Java呼叫dll&so動態庫的函式 *應用背景:專案中需要呼叫一項其他平臺提供的服務,是一位C語言老大哥寫的,可牛啦。但是因為一些特殊原因導致不能通過API呼叫的方式實現,最終的解決方案是老大哥把他的程式封裝成so和dll動態庫,然後我在專案裡使用JNA呼叫so或dll動態

Linux環境下 lua 呼叫自定義so動態(skynet)

最近看的 skynet 使用的 c+lua 的架構,框架提供的是基礎的api,所以業務邏輯還得自己去寫,如果某些業務邏輯比較耗效能,那可能就需要把某些業務邏輯丟到 c/c++ 去做,提供個介面供

C語言中呼叫靜態函式動態函式的方式

C語言中呼叫動態庫函式的兩種方式 方式一.隱式呼叫 將動態庫的相關檔案拷貝到當前目錄下(lib、dll),然後新增以下程式碼,在程式中指定連線庫函式。 注意:第二個引數給出的是引入庫檔案(或稱“匯出庫檔案”),而不是dll。在程式執行過程中,lib將dll中需要用到的函式對映到對應的記憶

java jni呼叫c/c++的so動態連結簡易demo

需求:            工作中需要多個團隊協作開發,而不同團隊提供的程式語言不同,比較常見的是底層互動使用C++或c來實現任務排程,java層實現業務排程或者業務實現,正好有涉及到這方面的應用,就分享一下java使用jni呼叫linux中so檔案的幾個注意事項編寫簡易的

c++呼叫c語言的so動態

1.環境  ubuntu14.04   gcc 4.8.4 g++ 4.8.4 2.庫檔案 1)原始碼 //clTest.c int add(int a, int b) { return a + b; } 2)生成庫檔案 cd到clTest.c所

C++中使用_asm彙編呼叫動態函式的一點問題

    因為從事dll 編寫的相關工作。沒寫完一個dll 之後都要對函式進行測試,對每個dll都要寫一個測試demo的話就非常費勁。能不能一個公共的測試軟體來各種dll裡的函式測試呢?     嘗試開始,從外界的.h檔案中讀取函式名很簡單,但是我們不能在程式已經編譯的過程中

C語言 呼叫動態函式重名問題分析

設計兩個動態庫 第一個動態庫:libHelloc: func1.h #ifndef FUNC1_H_ #define FUNC1_H_ int func1(); void func(); #endif func1.c #include "func1.h" int

C語言呼叫so動態的兩種方式

方式1:類似靜態庫的呼叫(使用標頭檔案) 這種方式生成的程式會在啟動時候就載入so動態庫。 add.h int add(int x, int y); add.c #include "add.h" int add(int x, int y

c++ 呼叫Python指令碼或者動態——環境Ubuntu 16.04下用codeblocks

背景:因為使用的是python版本的程式,最終要整合到C++環境的架構中,也就是說架構是c++的,交付使用者為c++的介面,但是呼叫的是python的庫,因此需要學習在c++環境下呼叫python。因為對python不熟悉,可以說有點一抹黑,因此從簡到難逐步探索。首先在c++

Java呼叫C++編寫的動態(JNI)

1. 在eclipse裡利用java建立一個類,在類中加入帶有native的方法(eclipse建立的類會自動新增class在bin目錄下) 2.利用javah命令生成本地方法的c++標頭檔案。進入到工程bin目錄下,按住shift,並右擊,選擇 “在此處開啟命令視窗”,進入命令視

PB呼叫.NET/C#開發的動態DLL的問題

之前用C#做了一個Dll提供給第三方呼叫(主要為PB使用者)。 pb一直無法呼叫我寫的dll,導致問題排查了3天。 最後找出原因是因為,對方環境沒有安裝.net framework。沒有將dll註冊導致的。

安卓so動態載入代理實現,可以實現C層的類反射效果

一般來說如果我們需要載入so檔案,需要一個java對應層的類,即有一個類必須要是包名和類名是不變的。 比如說下面的c層程式碼,這樣寫就必須要求有個類是com.example.hellojni.HelloJni,呼叫的方法為stringFromJNI /* DO NOT

利用c#實現dll動態,並在c++中呼叫的方法

           近期,在進行一個大專案開發。其中涉及多語言協同開發。主要是c#dll和c++dll的開發和應用,其中,需要在c++中呼叫c#dll的內容。現在把開發中的經驗、教訓和注意事項總結整理如下,希望對其他人能有所幫助。           1.建立c#dll,

VS2015用C++建立的動態匯出函式名亂碼原因分析

        在上一篇部落格【在VS2015中用C++建立動態庫並用C#呼叫】中提到,在C# DllImport匯入C/C++編寫的動態庫時函式,要加上CallingConvention = Ca

eclipse c/c++程式設計引用動態so

動態庫的引用: 右擊專案,選中屬性,在屬性頁中: C/C++ Build=》Settings=》Tool Settings=》GCC C Compiler=》command  欄寫上gcc -lpthread -ltcmrdriver     (使用者自定義庫libtcmrdriver.so) 同樣在GCC

C 語言兩個動態函式重名問題

應用程式a(a.c),動態庫liba.so(liba.h, liba.c),libb.so,均實現了func() gcc -la -lb a.c 則呼叫的是liba.so中的函式實現 gcc -lb -la a.c 則呼叫的是libb.so中的函式實現