JNI 编程初探

代码之美
Article Directory
  1. 流程

提起 Java,我们的耳边似乎总会响起这样的论调:

Java 使用虚拟机,性能效率不行,跟 C/C++ 没法比

好像说,Java 跟性能是绝缘的,C/C++ 才是性能的典范。那么,能否将两者结合起来呢?当然可以,就是我们今天的主题 —— Java JNI 。

简单地说,Java JNI 就是将 Java 类中的一些方法用 C/C++ 甚至是汇编来实现,以弥补 Java 某些方面的不足。下面我们通过一个简单的 Hello world 来阐述整个流程是怎么样的。

流程

首先,我们在 jni_first.java 中定义一个类,并且在类中声明一个方法,这个方法就是我们要使用 C/C++ 来实现的:

public class jni_first {
	static {
		System.loadLibrary("first");
	}
	public native void disp_jni();

	public static void main(String[] args) {
		new jni_first().disp_jni();
	}
}

**注意,上面的 System.loadLibrary("balabala") 里面的参数是有讲究的。他就是我们 native 方法所在的 C/C++ 文件编译之后的库的文件名。所以在 Windows 下,我们的 C/C++ 文件要编译为 balabala.dll ,在 Linux 下要编译为 libbalabala.so **,具体我们后面讲。

然后我们首先要将该 Java 文件编译成 class 文件:

javac jni_first.java

然后我们使用 javah 来生成头文件,头文件里面定义了我们 native 方法的函数名。也就是说,我们的 native 方法的函数名使不能让我们随便取的,而是要让机器生成的:

javah jni_first

接下来,我们就会发现我们当前的目录下出现了一个 jni_first.h 的文件,打开该文件看看它的内容:

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

#ifndef _Included_jni_first
#define _Included_jni_first
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jni_first
 * Method:    disp_jni
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jni_1first_disp_1jni
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

于是我们可以看到我们的 native 方法应该叫什么:Java_jni_1first_disp_1jni

接下来,实现我们的 native 方法:

#include "jni_first.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_jni_1first_disp_1jni(JNIEnv *, jobject)
{
        printf("From %s: ", __func__);
        printf("Hello, Java world.\n");

        return ;
}

编译成库:

g++ -shared -fPIC -I /usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux jni_first.cpp -o libfirs.so

注意这里的 -I 参数,第一个 -I 指定了 jni.h 文件的位置,第二个 -I 指定了 jni_md.h 文件的位置(在 jni.h 中被引用)。同时注意最后生成的库名,在 Windows 应该以 .dll 为后缀,在 Linux 应该以 lib 为前缀,.so 为后缀。

最后,执行:

java -Djava.library.path=. jni_first

这里的 -Djava.library.path=. 指定了库所在的位置是当前路径,不然会报错。于是,我们就可以看到以下输出了:

$ java -Djava.library.path=. jni_first
From Java_jni_1first_disp_1jni: Hello, Java world.

好了,至此,我们就把整个流程都走了一遍了。可以看到,Java JNI 和 Android JNI 的一个区别就是 Android JNI 可以通过 JNINativeMethod 数组来指定 native 方法的函数名,而 Java JNI 只能按照机器生成的头文件来。

EOF

Author: simowce

Permalink: https://blog.simowce.com/jni-programming/

知识共享许可协议
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。