Ubuntu22.04 JNI示例程序

70 阅读2分钟

创建一个java类

我们从一个Java类开始我们程序,我们需要定义一个名为Hello World的类,这个类需要在名为HelloWorld.java的Java源码文件,主要包含三个部分:

  1. 一个原生函数print的生命,由原生代码实现.
  2. 一个主函数,作为Java程序入口, 并在主函数中调用print.
  3. 一个静态代码块,用来加载动态链接库.
class HelloWorld {
  static {
    System.loadLibrary("HelloWorld");
  }
  private native void print();
  public static void main(String[] args) {
    new HelloWorld().print();
  }
}

print函数声明中的native修饰符,这个修饰符标示该函数是使用其他语言实现, 编译器不会因为其没有在Java代码中找到实现而报错.
包含原生代码的动态链接库一定要在原生函数被调用之前加载.

编译HelloWorld类

javac HelloWorld.java

这个命令将会在当前目录生成HelloWorld.class文件.

创建Native方法头文件

接下来我们需要使用javah工具生成JNI风格的头文件了,之后我们需要在c文件中实现相关的原生方法,你可以执行一下命令:

javac -h jni HelloWorld.java 

image.png

实现Native方法

javah生成的HelloWorld.h头文件清晰的罗列了native函数的函数原型,包括函数名,参数及返回值等,我们只需要按照这个函数原型实现其函数体就可以了.你可以使如下代实现HelloWorld.c

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <stdio.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_print
  (JNIEnv *, jobject) {
    printf("Hello JNI!\n");
  }

#ifdef __cplusplus
}
#endif
#endif

编译C源码并创建一个Native库

gcc -I/usr/lib/jvm/java-17-openjdk-amd64/include -I/usr/lib/jvm/java-17-openjdk-amd64/include/linux HelloWorld.c -shared -o libHelloWorld.so

image.png

执行程序

java -Djava.library.path=./jni HelloWorld

正确的设置动态链接库的路径对于程序成功运行只管重要,在加载时候需要将动态链接库所对应的目录加入系统LD_LIBRARY_PATH环境变量,或者在执行java命令的时候加入java.library.path选项,制定库所在目录.

image.png

参考文档