JNI定义


​ JNI是Java Native Interface的缩写,通过使用Java本地接口书写程序,可以确保代码在不同的平台上方便移植。

​ 很多底层功能或者硬件及驱动方面的功能是C/C++ 完成的, 想要和这些程序打交道的媒介就是JNI。


所需环境


1. Ubuntu 系统 (本文系统 16.04 LTS)

2. JDK (请自行安装)


JNI实现步骤


1. 编写java代码 (HelloJNI.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class HelloJNI{

//本地方法声明 (C中待会要实现的方法!!)
native void printHello();
native void printString(String str);

//加载库 (将后面生成的C文件制作成 libhellojni.so 的库)
static { System.loadLibrary("hellojni");}

public static void main(String args[]){
HelloJNI myJNI = new HelloJNI();

//调用本地方法*
myJNI.printHello();
myJNI.printString("hello world from printString fun");
}
}

2. 编译java代码

1
losedeer@xr:~/JNI_TEST$ javac HelloJNI.java

3. 编译映射C与Java之间的头文件

​ Java提供了 javah 命令 , 用来生成包含函数原型的C/C++的头文件. (可以理解为建立java 和 C 之间的连接规则!)

1
losedeer@xr:~/JNI_TEST$ javah HelloJNI 

​ 执行上述命令后,会生成一个.h 的头文件.(HelloJNI.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
29
30
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: printHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printHello
(JNIEnv *, jobject);

/*
* Class: HelloJNI
* Method: printString
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printString
(JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
~

4.根据头文件来实现C程序

​ 根据头文件规则:

img

​ 编写C程序:(hellojni.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include "HelloJNI.h"
#include "stdio.h"

//添加名称为env与obj的两个参数
JNIEXPORT void JNICALL Java_HelloJNI_printHello(JNIEnv *env, jobject obj)
{
printf("Hello XR,This is JNI.\n");
return;
}

JNIEXPORT void JNICALL Java_HelloJNI_printString(JNIEnv *env, jobject obj, jstring string)
{
//将 JAVA String 字符串转化为 C 字符串
const char *str = (*env)->GetStringUTFChars(env, string, 0);
printf("%s!\n", str);

return;
}

5. 制作C的静态库

Tips:

​ 1. /usr/lib/jvm/java-1.8.0-openjdk-amd64/include 的片段这个是你的JDK 安装的路径!

​ 2. 库文件命名规则!!! (Ubuntu 下为 libxxxx.so 文件 ! Windows 为 xxxx.dll 文件!)(xxxx为C的文件名)

1
gcc -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux hellojni.c -o libhellojni.so

​ 可以看到目录下生成了 libhellojni.so 文件


6. 运行Java程序(即可看见在C中实现的方法,可在JAVA中得到实现)

img


实现过程中 error 解决


第5步中:

​ 若出现一下报错:

/usr/bin/ld: /tmp/ccC9m80Y.o: relocation R_X86_64_32 against `.rodata’ can not be used when making a shared object; recompile with -fPIC /tmp/ccC9m80Y.o: 无法添加符号: 错误的值

解决方法:

​ 请第5步的命令之后添加 -fPIC 即:

1
gcc -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux hellojni.c -o libhellojni.so -fPIC

第6步中:

Exception in thread “main” java.lang.UnsatisfiedLinkError: no hellojni 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 HelloJNI.(HelloJNI.java:7)

解决方法

1. 在目录下编写输出默认读取库函数的路径:(例如 Test.java)

1
2
3
4
5
6

public class Test{
public static void main(String[] args){
System.out.println(System.getProperty("java.library.path"));
}
}

2. 运行看看默认的库函数路径

img

​ 可以看到我的库文件默认地址是:

​ /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib (这里有三个…… )

可以看到有三个地址,请自行查看你那个地址时存在的!!!!!!!!!!!!(本人只有 /usr/lib/ 是存在的)

​ 接下来把生成的 libhellojni.so 文件复制过去

1
losedeer@xr:~/JNI_TEST$ sudo cp libhellojni.so /usr/lib/

3. 继续尝试第6步