1. 前言
之前在公司开发的时候, 需要用到内部的组件, 尽管提供了jar包, 但是jar包底层是通过Java本地方法实现的, 所以仅仅导入jar包并不能正常使用, 还需要安装内部组件的环境. 所以写这篇文章记录Java本地方法的使用, 帮助自己熟悉这块的使用.
2. 基础使用
基于Linux环境, 这个章节简单介绍如何实现一个Java本地方法.
第一步: 建立一个普通的java类, 添加一个方法, 在方法返回值前加 native
1 | public class ClassMethod { |
第二步: 编译这个java类, 得到ClassMethod.class文件
1 | javac ClassMethod.java |
第三步: 找到当前类编译的.class文件, 使用命令行生成.h文件
1 | javah ClassMethod |
第四步: 查看ClassMethod.h
1 | /* DO NOT EDIT THIS FILE - it is machine generated */ |
第五步: 新建cpp文件, 并写逻辑代码(我这边命名为main.cpp,其实是可以随意命名的)
1 |
|
第六步: 将main.cpp文件和ClassMethod.h放到一个目录下, 并执行指令生成动态链接库.so文件
1 | gcc -fPIC -I /home/ganlu/java/jdk1.8.0_144/include/ -I /home/ganlu/java/jdk1.8.0_144/include/linux/ -shared -o libHello.so main.cpp |
第七步: 将动态链接库复制到java项目中
1 | public class ClassMethod { |
第八步: 重新编译ClassMethod.java并执行
1 | javac ClassMethod.java |
运行结果:
1 | hello world! |
3. 几个问题
3.1 动态链接库
我这边动态链接库的加载是通过绝对路径的指定来实现的, 而实际中, 这种绝对路径的指定肯定是不好的.
1 | System.load("/home/ganlu/java/workplace/libHello.so"); |
网上流传比较多的一种做法是如下(这里要注意, .so文件的命名一定要规范, 以lib开头, 以.so结尾):
1 | System.loadLibrary("Hello"); |
但是用这种方法直接运行, 会报错.
1 | Exception in thread "main" java.lang.UnsatisfiedLinkError: no Hello in java.library.path |
3.1.1 解决方法一:通过Java运行参数指定java.library.path
1 | java -Djava.library.path=. ClassMethod |
3.1.2 解决方法二:通过配置 LD_LIBRARY_PATH 环境变量
1 | sudo gedit /etc/profile |
3.2 工作流程图
3.3 常见异常
如果出现以下异常, 则说明.so文件不对, 是因为java 程序在调用.so时候, 不能找到native method 方法的实现.
主要是因为在.so中, 可能xxx.h头文件中的接口名和xxx.cpp源文件中的实现函数名不一致导致的.
1 | Exception in thread "main" java.lang.UnsatisfiedLinkError: test.ClassMethod.greeting()V |
4. 参考链接
Java native 本地方法调用
java 本地方法(JNI)
Exception in thread “main” java.lang.UnsatisfiedLinkError: xxx()V