Java JNI
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++文件。