最近项目收到个需求,需要调用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函数获取压缩对象
}