初识JNA

  JNA(Java Native Access)对JNI进行了包装,提供了一组工具类用于访问本地程序,不需要编写任何Native或JNI代码。只需要在Java接口文件中定义与本地类库中的方法结构一样的native类型的方法即可,不需要实现,JNA会自动联系Java中的接口与本地类库中的方法。

  原则上来说在有本地类库时,并不需要再写本地代码,而是直接写Java代码即可,但这样做的前提是要有一个编写良好又明确标明方法的类库。如果碰到任务需要在原来本地类库的基础上进行二次包装,就不得不继续编写本地代码了,而我写的这个例子就是按照这样的步骤来的。

步骤

步骤1:
新建一个接口文件,定义需要调用的本地方法。

步骤2:
编写本地方法,用C/C++实现Java接口中的方法。方法声明前要加上 __declspec(dllexport) 即把声明的方法对外暴露。

步骤3:
生产动态库,放到对应目录下。本程序中,在Windows下,生成了dll文件,放到了JDK的bin中。

步骤4:
加载动态库,用JNA对于接口的实例来调用接口中的方法。

分步实现

新建接口类,定义接口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.sun.jna.Library;
/**
* JNA的接口类
*/
public interface JNADemo extends Library {
/**
* 无返回值,无参方法测试
*/
void test1();
/**
* 有返回值,有参方法测试
*
* @param strArg
* @return
*/
String test2(String strArg);
}

    这个接口一定要继承Library的父类,对于所有的本地类库定义都源自于这个类。

编写本地方法

头文件jnainterface.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#pragma once
#include "stdafx.h"
#include <string>
#include <iostream>
#ifndef _Included_jnainterface
#define _Included_jnainterface
#ifdef __cplusplus
extern "C" {
#endif
using namespace std;
/************************************************************************/
/* 无返回值,无参测试方法 */
/************************************************************************/
__declspec(dllexport) void test1();
/************************************************************************/
/* 有返回值,有参测试方法 */
/************************************************************************/
__declspec(dllexport) char* test2(char* strArg);
#ifdef __cplusplus
}
#endif
#endif

注意方法声明的前面添加了__declspec(dllexport)。

源码文件jnainterface.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "stdafx.h"
#include "jnainterface.h"
/************************************************************************/
/* 无返回值,无参测试方法 */
/************************************************************************/
void test1() {
cout << "Hello World, this is C++ printer." << endl;
}
/************************************************************************/
/* 有返回值,有参测试方法 */
/************************************************************************/
char* test2(char* strArg) {
// 打印来自Java的字符串
cout << strArg << endl;
// 向Java端返回字符串
return "From C++ dll.";
}

生成动态库

    本地代码完成后,随即就要编译为本地类库,并放到对应目录下。本程序中,在windows下,我把生成的dll文件放到了JDK的bin目录下。

加载动态库

    加载动态库,用JNA对于接口的实例来调用接口中的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import com.sun.jna.Native;
/**
* JNA测试
*/
public class Test {
@org.junit.Test
public void testEasemob() throws InterruptedException {
JNADemo jnaDemo = Native.loadLibrary("JavaCpp", JNADemo.class);
// 调用Java接口中的方法,执行C++中的方法体
jnaDemo.test1();
// 向C++类库中传递字符串参数,并得到字符串返回值
String cpprt = jnaDemo.test2("FROM Java");
System.out.println("来自C++动态类库:" + cpprt);
}
}

    执行上面的代码,得到下面的结果。

注意

    不管是使用JNI还是JNA都要特别注意Java与本地类库中类型的对应。在JNA的网站上给出了Java与C的类型对照,可以做参考。

    对于其中的boolean我也用过,在C++中因为有boolean类型,所以C++代码中可以直接用boolean。

大爷给小弟的零花钱
显示 Gitment 评论