Java的JNI入门HelloWorld

JNI(Java Native Interface)是从开始Java1.0提供的一种在JVM中调用本地方法
的机制

本地方法是指C/C++的函数

Java代码

使用native关键字定义本地本地方法

使用System.load()或者System.loadLibrary()方法加载本地方法的动态链接库

  • load()方法用于加载指定的动态链接库,参数为动态链接库全路径
  • loadLibrary()方法用于加载特定目录(比如系统的默认存储动态链接库的目录)下的动态链接库,参数为动态链接库的名称
package space.anyi.jni.helloWorld;

public class Main {
    static {
        //加载动态连接库
        //load用于加载指定的动态链接库,参数为动态链接库全路径
        System.load("/home/yangyi/JNI-learn/c/helloWorld/JNI_Hello_World.so");
        //loadLibrary用于加载特定目录(比如系统的默认存储动态链接库的目录)下的动态链接库,参数为动态链接库的名称
        //可以在启动JVM时添加参数-Djava.library.path参与指定加载动态链接库的目录
//        System.loadLibrary("JNI_Hello_World.so");
    }
    public static void main(String[] args) {
        //调用本地方法
        staticHelloWorld();
        new Main().instanceHelloWorld();
    }
    //定义一个静态的本地方法
    public static native void staticHelloWorld();
    //定义一个普通的本地方法
    public native void instanceHelloWorld();
}

编译Java代码生成JNI需要的头文件

使用javac -h ${workDir}/c/helloWorld/ ${workDir}/space/anyi/jni/helloWorld/Main.java命令编译并生成对应定义本地方法的头文件

得到名为"space_anyi_jni_helloWorld_Main.h"对应的头文件

头文件的命名规则为:包名_类名.h

包名中分割符点"."使用下划线"_"替代

头文件中本地方法的命名规则为:Java_包名_类名_方法名

完整的方法签名为:JNIEXPORT 方法返回类型 JNICALL Java_包名_类名_方法名

本地方法有两个固定的参数

  1. 第一个参数
    • JNIEnv*:环境变量的指针
  2. 第二个参数
    • jclass:jclass为class对象对应的结构体

    • jobject:jobject为实例对象对应的结构体

  • 当本地方为静态方法时,第二个参数为jclass
  • 当本地方法为实例普通方法时,第二个参数为jobject

头文件中使用了"jni.h"头文件,该头文件由JDK提供,在JDK的"include"目录中,后续编译时需要使用

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

#ifndef _Included_space_anyi_jni_helloWorld_Main
#define _Included_space_anyi_jni_helloWorld_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     space_anyi_jni_helloWorld_Main
 * Method:    staticHelloWorld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_space_anyi_jni_helloWorld_Main_staticHelloWorld
  (JNIEnv *, jclass);

/*
 * Class:     space_anyi_jni_helloWorld_Main
 * Method:    instanceHelloWorld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_space_anyi_jni_helloWorld_Main_instanceHelloWorld
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

实现本地方法

导入头文件"space_anyi_jni_helloWorld_Main.h"

并实现头文件中定义的函数

//导入头文件
#include "space_anyi_jni_helloWorld_Main.h"
#include <stdio.h>
//实现静态本地方法
JNIEXPORT void JNICALL Java_space_anyi_jni_helloWorld_Main_staticHelloWorld(JNIEnv *, jclass){
    printf("static  Hello World!\n");
}
//实现实例普通本地方法
JNIEXPORT void JNICALL Java_space_anyi_jni_helloWorld_Main_instanceHelloWorld(JNIEnv *, jobject){
    printf("instance  Hello World!\n");
}

编译本地方法的代码

编译C代码

gcc -c -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -o c/helloWorld/JNI_Hello_World.o c/helloWorld/JNI_Hello_World.c

在目录"c/helloWorld"下产生编译结果"JNI_Hello_World.o"

将编译产物编译为动态链接库

gcc -shared -fPIC -o c/helloWorld/JNI_Hello_World.so c/helloWorld/JNI_Hello_World.o -lc

在目录"c/helloWorld"下产生编译结果"JNI_Hello_World.so"

运行测试

java -cp src/main/java/ space.anyi.jni.helloWorld.Main

输出结果

yangyi@debain12:~/JNI-learn$ java -cp src/main/java/ space.anyi.jni.helloWorld.Main
static  Hello World!
instance  Hello World!

参考资料

JNI 使用笔记
案例代码仓库