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++类型 原生表现
boolean int 32位整数(可定制)
byte char 8位整数
char wchar_t 平台依赖
short short 16位整数
int int 32位整数(可定制)
long ong long, __int64 64位整数
float float 32位浮点数
double double 64位浮点数
Buffer/Pointer pointer 平台依赖(32或64位指针)
[] (基本类型的数组) pointer/array 2或64位指针(参数/返回值) 邻接内存(结构体成员)
String char* 0结束的数组 (native encoding or jna.encoding)
WString wchar_t* /0结束的数组(unicode)
String[] char** /0结束的数组的数组
WString[] wchar_t** /0结束的宽字符数组的数组
Structure struct*/struct 指向结构体的指针(参数或返回值) (或者明确指定是结构体指针)结构体(结构体的成员) (或者明确指定是结构体)
Union union 等同于结构体
Structure[] struct[] 结构体的数组,邻接内存
Callback (*fp)() Java函数指针或原生函数指针
NativeMapped varies 依赖于定义
NativeLong long 平台依赖(32或64位整数)
PointerType pointer 和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();
};
};

猜你喜欢

转载自blog.csdn.net/qq_42133100/article/details/102685530
今日推荐