Java本地方法


1. 前言

之前在公司开发的时候, 需要用到内部的组件, 尽管提供了jar包, 但是jar包底层是通过Java本地方法实现的, 所以仅仅导入jar包并不能正常使用, 还需要安装内部组件的环境. 所以写这篇文章记录Java本地方法的使用, 帮助自己熟悉这块的使用.

2. 基础使用

基于Linux环境, 这个章节简单介绍如何实现一个Java本地方法.

第一步: 建立一个普通的java类, 添加一个方法, 在方法返回值前加 native

1
2
3
4
public class ClassMethod {
//本地方法可以是静态的也可以是非静态的;
public static native void greeting();
}

第二步: 编译这个java类, 得到ClassMethod.class文件

1
javac ClassMethod.java

第三步: 找到当前类编译的.class文件, 使用命令行生成.h文件

1
javah ClassMethod

第四步: 查看ClassMethod.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ClassMethod */

#ifndef _Included_ClassMethod
#define _Included_ClassMethod
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: ClassMethod
* Method: greeting
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_ClassMethod_greeting
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

第五步: 新建cpp文件, 并写逻辑代码(我这边命名为main.cpp,其实是可以随意命名的)

1
2
3
4
5
#include "ClassMethod.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_ClassMethod_greeting(JNIEnv *, jclass){
printf("hello world!");
}

第六步: 将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
2
3
4
5
6
7
8
9
10
11
12
public class ClassMethod {
static
{
System.load("/home/ganlu/java/workplace/libHello.so");
}
//本地方法可以是静态的也可以是非静态的;
public static native void greeting();

public static void main(String args[]){
greeting();
}
}

第八步: 重新编译ClassMethod.java并执行

1
2
javac ClassMethod.java
java ClassMethod

运行结果:

1
hello world!

Java本地方法

3. 几个问题

3.1 动态链接库

我这边动态链接库的加载是通过绝对路径的指定来实现的, 而实际中, 这种绝对路径的指定肯定是不好的.

1
System.load("/home/ganlu/java/workplace/libHello.so");

网上流传比较多的一种做法是如下(这里要注意, .so文件的命名一定要规范, 以lib开头, 以.so结尾):

1
System.loadLibrary("Hello");

但是用这种方法直接运行, 会报错.

1
2
3
4
5
Exception in thread "main" java.lang.UnsatisfiedLinkError: no Hello in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at ClassMethod.<clinit>(ClassMethod.java:4)

3.1.1 解决方法一:通过Java运行参数指定java.library.path

1
java -Djava.library.path=. ClassMethod

3.1.2 解决方法二:通过配置 LD_LIBRARY_PATH 环境变量

1
2
3
sudo gedit /etc/profile

export LD_LIBRARY_PATH=/home/ganlu/java/workplace

3.2 工作流程图

工作流程图

3.3 常见异常

如果出现以下异常, 则说明.so文件不对, 是因为java 程序在调用.so时候, 不能找到native method 方法的实现.
主要是因为在.so中, 可能xxx.h头文件中的接口名和xxx.cpp源文件中的实现函数名不一致导致的.

1
2
3
Exception in thread "main" java.lang.UnsatisfiedLinkError: test.ClassMethod.greeting()V
at test.ClassMethod.greeting(Native Method)
at test.ClassMethod.main(ClassMethod.java:11)

4. 参考链接

Java native 本地方法调用
java 本地方法(JNI)
Exception in thread “main” java.lang.UnsatisfiedLinkError: xxx()V

谢谢你请我吃糖果!