java使用jna基于linux调用c++动态函数库(简单调用,基本传参数,模拟结构体传参(值&引用),回调java方法)

记一次jna调研成果,动机是公司开始做一个公安交通方面的项目,对接华为以及大华时,他们提供的sdk基本都是函数库,所以才有了这次“轰轰烈烈”的调研。
因为目前这方面的资料比较少,所以在调研过程中还是踩了不少坑,将这次成果记录下来,有需要的人可以借鉴一下,基本能满足需要

jna简述

JNA全称Java Native Access,是一个建立在经典的JNI技术之上的Java开源框架。
JNA提供工具用于调用c/c++动态函数库(如Window的dll以及linux的so)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标函数库的函数与结构,JNA将自动实现Java接口方法到函数的映射

jna&c++数据类型对照

java类型c++类型原生表现
booleanint32位整数(可定制)
bytechar8位整数
charwchar_t平台依赖
shortshort16位整数
intint32位整数(可定制)
longong long, __int6464位整数
floatfloat32位浮点数
doubledouble64位浮点数
Buffer/Pointerpointer平台依赖(32或64位指针)
[] (基本类型的数组)pointer/array2或64位指针(参数/返回值) 邻接内存(结构体成员)
Stringchar*0结束的数组 (native encoding or jna.encoding)
WStringwchar_t*/0结束的数组(unicode)
String[]char**/0结束的数组的数组
WString[]wchar_t**/0结束的宽字符数组的数组
Structurestruct*/struct指向结构体的指针(参数或返回值) (或者明确指定是结构体指针)结构体(结构体的成员) (或者明确指定是结构体)
Unionunion等同于结构体
Structure[]struct[]结构体的数组,邻接内存
Callback (*fp)()Java函数指针或原生函数指针
NativeMappedvaries依赖于定义
NativeLonglong平台依赖(32或64位整数)
PointerTypepointer和Pointer相同

使用示例

环境说明:

  • jna-version:5.2.0
  • jdk-version:1.8
  • c++version:c++11
  • java开发使用idea
  • c++开发使用clion
  • g++版本自选
  • 下面是jna的maven坐标,也可以将jar下载到本地使用
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.2.0</version>
        </dependency>

1、简单调用

实现步骤

  • 创建一个接口TestNative继承Library
  • 创建一个本地方法(用native修饰的方法)cppHello()
  • 创建头文件 library.h,定义一个函数cppHello();
  • 创建cpp文件 library.cpp,实现头文件中的cppHello();
  • 执行命令生成liblibrary.so动态库
    g++ library.cpp -fPIC -shared -o liblibrary.so
  • 接口内用Native.load()方法加载函数库(使用绝对路径)创建一个TestNative实例
  • TestNative.testNative.cppHello();进行调用

java代码

import com.sun.jna.*;

import java.util.Arrays;
import java.util.List;

/**
 * @Author :feiyang
 * @Date :Created in 11:14 AM 2019/10/14
 */
public interface TestNative extends Library {
   TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
   /**
    * 
    * @author  feiyang
    * @return  void 
    * @date    2019/10/22
    * @throws
    */ 
   void cppHello();
   
   /**
     * 测试方法
     * @param args
     */
   static void main(String[] args) {
        TestNative.testNative.cppHello();
    }
   }

c++代码

头文件 library.h

#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H

#include <iostream>

extern "C" {
    using namespace std;
    void cppHello();
};
#endif

cpp文件 library.cpp

#include "library.h"
#include <iostream>
extern "C" {
void cppHello() {
    using namespace std;
    cout << "cpp执行:cppHello!" << endl;
};
};

2、基本传参

实现步骤

  • 同上

java代码

import com.sun.jna.*;

import java.util.Arrays;
import java.util.List;

/**
 * @Author :feiyang
 * @Date :Created in 11:14 AM 2019/10/14
 */
public interface TestNative extends Library {
   TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
   
   /**
    * 
    * @author  feiyang 
    * @param basic
    * @param message
    * @return  void 
    * @date    2019/10/22
    * @throws
    */
   void cppHello(int basic, String message);

   /**
     * 测试方法
     * @param args
     */
   static void main(String[] args) {
   int basic = 1;
   String message = "我是java方,呼叫cpp";
        TestNative.testNative.cppHello(basic, message);
    }
   }

c++代码

头文件 library.h

#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H

#include <iostream>

extern "C" {
    using namespace std;
    void cppHello(int basic, char * message);
};
#endif

cpp文件 library.cpp

#include "library.h"
#include <iostream>
extern "C" {
void cppHello(int basic, char * message) {
    using namespace std;
    cout << "cpp收到:" << basic << endl;
    cout << "cpp收到:" << message << endl;
};
};

3、模拟结构体传参

实现步骤

  • 同上
  • 创建一个类TestStruct继承Structure,设置两个String类型的成员变量
    filedOne、filedTwo;
  • 创建两个内部类ByReference、ByValue,继承TestStruct,分别实现接口Structure.ByReference、Structure.ByValue
  • 覆盖getFieldOrder()方法
  • 头文件定义结构体,方法定义增加形参
  • cpp文件实现这个方法

结构体传参分为值传递、引用传递(指针传递)

  • 值传递使用ByValue
  • 引用传递使用ByReference

值传递

java代码

import com.sun.jna.*;

import java.util.Arrays;
import java.util.List;

/**
 * @Author :feiyang
 * @Date :Created in 11:14 AM 2019/10/14
 */
public interface TestNative extends Library {
   TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
   
   /**
    * 本地方法
    * @author  feiyang
    * @param testValue
    * @return  void
    * @date    2019/10/22
    * @throws
    */
   void cppHello(TestStruct.ByValue testValue);

   class TestStruct extends Structure {
        public String filedOne;
        public String filedTwo;

        public static class ByReference extends TestStruct implements Structure.ByReference {
        }

        public static class ByValue extends TestStruct implements Structure.ByValue {
        }

        @Override
        public String toString() {
            return "TestStruct{" +
                    "filedOne='" + filedOne + '\'' +
                    ", filedTwo='" + filedTwo + '\'' +
                    '}';
        }

        @Override
        protected List getFieldOrder() {
            return Arrays.asList(new String[] { "filedOne", "filedTwo"});
        }

    }
   /**
     * 测试方法
     * @param args
     */
   static void main(String[] args) {
   TestStruct.ByValue testValue = new TestStruct.ByValue();
        testValue.filedOne = new String("11111");
        testValue.filedTwo = new String("22222");
        TestNative.testNative.cppHello(testValue);
        System.out.println(testValue);
    }
   }

c++代码
头文件 library.h

#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H

#include <iostream>

extern "C" {
    using namespace std;
    struct TestStruct {
         char *filedOne;
         char *filedTwo;
    };
    void cppHello(TestStruct testStruct);
};
#endif

cpp文件 library.cpp

#include "library.h"
#include <iostream>
extern "C" {
void cppHello(TestStruct testStruct) {
    using namespace std;
    cout << "value:filedOne:" << testStruct.filedOne << endl;
    cout << "value:filedTwo:" << testStruct.filedTwo << endl;
    testStruct.filedOne = "update";
    testStruct.filedTwo = "update";
};
};

引用传递

java代码

import com.sun.jna.*;

import java.util.Arrays;
import java.util.List;

/**
 * @Author :feiyang
 * @Date :Created in 11:14 AM 2019/10/14
 */
public interface TestNative extends Library {
   TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
   
   /**
    * 本地方法
    * @author  feiyang
    * @param testReference
    * @return  void
    * @date    2019/10/22
    * @throws
    */
   void cppHello(TestStruct.ByReference testReference);

   class TestStruct extends Structure {
        public String filedOne;
        public String filedTwo;

        public static class ByReference extends TestStruct implements Structure.ByReference {
        }

        public static class ByValue extends TestStruct implements Structure.ByValue {
        }

        @Override
        public String toString() {
            return "TestStruct{" +
                    "filedOne='" + filedOne + '\'' +
                    ", filedTwo='" + filedTwo + '\'' +
                    '}';
        }

        @Override
        protected List getFieldOrder() {
            return Arrays.asList(new String[] { "filedOne", "filedTwo"});
        }

    }
   /**
     * 测试方法
     * @param args
     */
   static void main(String[] args) {
   TestStruct.ByReference testReference = new TestStruct.ByReference();
        testReference.filedOne = new String("11111");
        testReference.filedTwo = new String("22222");
        TestNative.testNative.cppHello(testReference);
        System.out.println(testReference);
    }
   }

c++代码
头文件 library.h

#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H

#include <iostream>

extern "C" {
    using namespace std;
    struct TestStruct {
         char *filedOne;
         char *filedTwo;
    };
    void cppHello(TestStruct *testStruct);
};
#endif

cpp文件 library.cpp

#include "library.h"
#include <iostream>
extern "C" {
void cppHello(TestStruct *testStruct) {
    using namespace std;
    cout << "reference:filedOne:" << testStruct -> filedOne << endl;
    cout << "reference:filedTwo:" << testStruct -> filedTwo << endl;
    testStruct1 -> filedOne = "update";
    testStruct1 -> filedTwo = "update";
};
};

注意点

  • 值传递时,当cpp代码中对结构体的成员变量进行修改时不会影响到java中的模拟结构体,因为值传递只是拷贝值进行传递
  • 引用传递时,当cpp代码中对结构体的成员变量进行修改时是会影响到java中的模拟结构体的,因为引用传递是进行引用地址的传递

回调java方法

实现步骤

  • 同上
  • 创建一个接口CallBack继承Callback
  • 在接口内创建一个方法void invoke();
  • 创建一个类实现CallBack,实现invoke方法
  • 在本地cppHello()方法设置一个CallBack的形参,将回调函数通过类进行传递
  • 在头文件中定义回调函数,并设置cppHello()的函数形参
  • 在cpp文件实现cppHello方法

java代码

import com.sun.jna.*;

import java.util.Arrays;
import java.util.List;

/**
 * @Author :feiyang
 * @Date :Created in 11:14 AM 2019/10/14
 */
public interface TestNative extends Library {
   TestNative testNative = Native.load("/Users/liblibrary.so", TestNative.class);
   
   /**
    * 本地方法
    * @author  feiyang
    * @param callBack
    * @return  void
    * @date    2019/10/22
    * @throws
    */
   void cppHello(CallBack callBack);

    interface CallBack extends Callback {
        void invoke();
    }
    class CallBackImpl implements CallBack {

        @Override
        public void invoke() {
            System.out.println("回调成功");
        }
     }

     /**
     * 测试方法
     * @param args
     */
    static void main(String[] args) {
        TestNative.testNative.cppHello(new CallBackImpl());
    }
}

c++代码
头文件 library.h

#ifndef UNTITLED1_LIBRARY_H
#define UNTITLED1_LIBRARY_H

#include <iostream>

extern "C" {
    using namespace std;
    typedef void(*javaCallBack)();
    void cppHello(javaCallBack javacallback);
};
#endif

cpp文件 library.cpp

#include "library.h"
#include <iostream>
extern "C" {
void cppHello(javaCallBack javacallback) {
    using namespace std;
    javacallback();
};
};
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值