https://www.jianshu.com/p/ead89497c403
只要你用过了 JNA (java native access) , 那你可能就再也不想用 JNI 了
实际上, JNA 搞定了 JNI 中最麻烦的数据类型映射, 可以让我们进行高效的开发, 不用再去写各种的转换接口.
上面的数据类型它都支持
可能有人会问 JNA 能完全代替 JNI 么? 不能, JNA只能实现Java访问C函数,如果你想实现C语言调用 Java 代码, 你还是需要使用 JNI 技术。
但其实在很大程度上已经够用了, 因为在很多应用领域都是由 C++算法工程师提供库 (.so/.dll) , java 工程师只需要负责调用就可以了
就一个 简化 jni 开发
想想当年写 jni 的时候, 下面这样的代码要写几百行, 你就知道我有多痛苦了
当然最痛苦的还不是这个
在开发中, 作为一个JAVA 程序员你会遇到各种各样奇葩的问题
还包含一部分你无法理解的C++问题
而如果使用 JNA, 你可能只需要这样:
你不需要通过 javah 生成头文件, 不需要给它写实现
不需要在 windows/linux 环境各自编译成 .dll/.so 来调用真正的函数
只需要声明一个接口, 其他的事情让 JNA 做好就行
public class HelloWorld {
public interface CLibrary extends Library {
CLibrary INSTANCE = Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
}
}
// 该源码来自 https://github.com/java-native-access/jna/blob/master/www/GettingStarted.md
...
每天都在用的 IDEA 都用了 JNA =.= 你还怕什么?
了解一下 C -> java 的数据类型映射, 帮助你更好的完成之后的练习
Native Type | Size | Java Type | Common Windows Types |
---|---|---|---|
char | 8-bit integer | byte | BYTE, TCHAR |
short | 16-bit integer | short | WORD |
wchar_t | 16/32-bit character | char | TCHAR |
int | 32-bit integer | int | DWORD |
int | boolean value | boolean | BOOL |
long | 32/64-bit integer | NativeLong | LONG |
long long | 64-bit integer | long | __int64 |
float | 32-bit FP | float | |
double | 64-bit FP | double | |
char* | C string | String | LPCSTR |
void* | pointer | Pointer | LPVOID, HANDLE, LPXXX |
from JNA - Default Type Mappings
你可能会发现表中没有 native 的 boolean 类型, 默认上 boolean = true
会被默认映射成 -1(int)
, 在 C 中打印出来是 255. 所以我一般在定义接口时, 会避免使用 bool . 如果想自定义 boolean 映射, 可以参考 JNA maps Java boolean to -1 integer?
本代码示例基于 64位 win10 + vs 2019
如果遇到 dll 依赖问题( UnsatisfiedLinkError ), 请下载 micro soft vc 运行库 或者使用 Dependency walker 查看 dll 依赖缺失情况
可以 clone 一下我的项目:
java 部分: https://github.com/giraffe-tree/jna-func
c++ 部分(vs 项目): https://github.com/giraffe-tree/jna-c
先来个简单的例子
先将下面的依赖加入你的 java maven 项目
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.3.1</version>
</dependency>
// c++ 需要在 vs 中编译成 dll
int max(int num1, int num2) {
return num1 > num2 ? num1 : num2;
}
// java
public interface JnaLibrary extends Library {
// JNA 为 dll 名称
JnaLibrary INSTANCE = Native.load("JNA", JnaLibrary.class);
int max(int a, int b);
}
将编译好的 JNA.dll
放入 resources 文件夹下的 win32-x86-64
目录中 (我使用的是 64位 windows), JNA 会自动到 win32-x86-64
中去找 JNA.dll
运行 main
函数即可
public static void main(String[] args) {
int max = JnaLibrary.INSTANCE.max(100, 200);
// out: 200
System.out.println(max);
}
// java
public interface JnaLibrary extends Library {
// JNA 为 dll 名称
JnaLibrary INSTANCE = Native.load("JNA", JnaLibrary.class);
void testArray(short[] vals, int len);
}
// c++
void testArray(uint16_t* vals, int len) {
for (int j = 0; j < len; j++) {
printf("vals[%d]: %d \n", j, vals[j]);
}
}
JnaLibrary.INSTANCE.testArray(new short[]{1, 2, 3, 4}, 4);
// out:
vals[0]: 1
vals[1]: 2
vals[2]: 3
vals[3]: 4
通过值传递对象的时候需要注意
Structure
, 且它的属性必须为 public
Invalid memory access
的异常, 这时候最好实现一下 Structure.ByValue
接口Invalid memory access
// java
public interface JnaLibrary extends Library {
// JNA 为 dll 名称
JnaLibrary INSTANCE = Native.load("JNA", JnaLibrary.class);
// 实际测试下来 void printUser(User.ByValue user); 也是可以的
void printUser(User user);
void printUserRef(User user);
@Structure.FieldOrder({"name", "height", "weight"})
public static class User extends Structure {
public static class UserValue extends User implements Structure.ByValue {
public UserValue(String name, int height, double weight) {
super(name, height, weight);
}
}
public User(String name, int height, double weight) {
this.name = name;
this.height = height;
this.weight = weight;
}
public String name;
public int height;
public double weight;
}
}
// .h
struct User {
char* name;
int height;
double weight;
};
void printUser(User user);
void printUserRef(User& user);
// cpp
void printUser(User user) {
printf("printUser user: %s height: %d weight: %.2f \n", user.name, user.height, user.weight);
}
void printUserRef(User& user) {
printf("printUserRef user: %s height: %d weight: %.2f \n", user.name, user.height, user.weight);
}
JnaLibrary.User.UserValue user1 = new JnaLibrary.User.UserValue("user1", 186, 65.2);
JnaLibrary.INSTANCE.printUserRef(user1);
JnaLibrary.INSTANCE.printUser(user1);
// out:
printUserRef user: user1 height: 186 weight: 65.20
printUser user: user1 height: 186 weight: 65.20
// java
public interface JnaLibrary extends Library {
// JNA 为 dll 名称
JnaLibrary INSTANCE = Native.load("JNA", JnaLibrary.class);
void testStruct(ArrInfo arrInfo);
@Structure.FieldOrder({"vals", "len"})
public static class ArrInfo extends Structure {
public Pointer vals;
public int len;
public ArrInfo(Pointer vals, int len) {
this.vals = vals;
this.len = len;
}
}
}
// .h
struct ArrInfo
{
uint16_t* vals;
int len;
};
void testStruct(ArrInfo arrInfo);
// cpp
void testStruct(ArrInfo arrInfo) {
for (int j = 0; j < arrInfo.len; j++) {
printf("arrInfo[%d]: %d \n", j, arrInfo.vals[j]);
}
}
// java main test
int len = 3;
int shortSize = Native.getNativeSize(Short.class);
Pointer pointer = new Memory(len * shortSize);
for (int i = 0; i < len; i++) {
pointer.setShort(shortSize * i, (short) i);
}
JnaLibrary.ArrInfo arrInfo = new JnaLibrary.ArrInfo(pointer, len);
JnaLibrary.INSTANCE.testStruct(arrInfo);
// out
arrInfo[0]: 0
arrInfo[1]: 1
arrInfo[2]: 2
虽然 JNA 相比于 JNI 好用很多, 但我在使用的过程中还是遇到一些"坑"
这些 bug 常常很难直接找出, JNA 统一都报了一个 Invalid memory access
, 导致我们找不到真正错误的原因. 这时候就需要调试了
我使用的开发环境是 IDEA + jdk8 + VS2019
先简单讲下步骤, 具体图文可以看下面的实战
resources/win32-x86-64/JNA.dll
)jps -l
找到 java 程序的 pidctrl+alt+p
附加到进程, 选择刚刚找到的 pid, 点击附加
j
可以快速查找在 idea 中进入调试
在下图中, jps -l
找到 java 程序的 pid = 10560
在 vs 中 ctrl+alt+p
附加到进程, 选择刚刚找到的 pid, 点击附加
此时断点不会命中
接着在 idea 中点击继续, 然后回到 vs 界面, 我们发现 断点被触发了
查看局部变量
yeah, 调试完成 =.=
目前测试下来, 可能存在 java 主线程停止, 但 C++ print 缓存区未被清空/不输出的情况, 导致控制台打印内容缺失
在这种情况下, 请延长 java 主线程运行的时间.
目前测试下来, 在 vs 中 debug 继续后会报出一个 未加载 jvm.pdb
, 我确实没有找到这个文件
但还可以继续调试, 没啥大问题
不过什么时候来个调试 openjdk 想想应该蛮有趣的 哈哈
这个问题怎么说呢, JNA 好多地方都能报出这个错误, 我遇到这个错误时, 大部分都是我的java 参数 -> C++ 参数的映射问题
包括参数类型写错, 顺序写错等, 仔细检查映射关系就能解决
java.lang.UnsatisfiedLinkError: 找不到指定的模块。
这个错误我遇到过的情况分两种
win32-x86-64
, 但是没有放入这个目录中个人网站文章链接: https://giraffetree.me/2019/10/28/jna_tutorial/ 欢迎留言~