使用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文件放置路径

二、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函数获取压缩对象
    }

猜你喜欢

转载自blog.csdn.net/shmely/article/details/82350062