潘瑞峰的个人博客

Java JNI

潘瑞峰 javaJNI

JNI

JNI是Java Native Interface的简称,它可以调用其他语言编写的程序,主要是C和C++,具体的介绍网上有很多,就不再重复了

我以前都是通过协议接口,HTTP、Socket等方式,实现Java调用其他程序的(松耦合、程序之间只关注接口格式),所以从来没用过JNI

JNI Java部分代码

import java.lang.reflect.Field;
public class Main {
    
    //declare native method
    public native int add(int a, int b);

    static {
        try {
            //set native c++ lib path
            System.setProperty("java.library.path", System.getProperty("java.library.path") + ":/Users/dapan/Desktop/JniTest/jnilib");
            Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
            fieldSysPath.setAccessible(true);
            fieldSysPath.set(null, null);
            
            //set native c++ lib
            System.loadLibrary("JNIADD");
        } catch(Exception e) {
            //print Error
            e.printStackTrace();
        }
        
    }
    public static void main(String[] args) {
        
        Main main = new Main();
        int c = main.add(2, 3);
        System.out.println(c);
    }
}

可以看出,我申明了一个native方法add,但是没有实现它。然后我设置了native C++库的路径,并且载入lib->JNIADD

在主函数中,我调用的add函数

之后,可以先编译函数javac Main.java,在编译时,我还没实现add函数

生成JNI的头文件,并且实现

执行javah Main后,会生成Main.h文件

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

#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Main
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_Main_add
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

这个头文件就是Java调用C++的接口,所以需要实现这个头文件,因此我新建了一个Main.cpp的文件

#include "Main.h"

JNIEXPORT jint JNICALL Java_Main_add
  (JNIEnv *env, jobject obj, jint a, jint b) 
  {
    return a + b ;
  }

编译C++文件,并且打包成lib(mac版)

  • 编译cpp文件: g++ -I $JAVA_HOME/include -c Main.cpp,编译完成后会生成Main.o文件
  • 需要将Main.o打包成jnilib文件: g++ -dynamiclib -o libJNIADD.jnilib Main.o 打包的jnilib命名规范是libxxx.jnilib,其中xxx是我在Java中System.loadLibrary("JNIADD")设置的library的名称
  • 最后将打包的jnilib放到Java中设置的java.library.path目录之一即可

结果

最后就可以用java Main运行java程序了

以C++为例,在运行过程中,可以随时重新编写打包C++程序,使得程序变得很灵活;但是其可移植性会变差,因为对于不同的平台,需要重新编译C++文件。

潘瑞峰
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。