java是一门被广泛使用的语言,接触过大数据的都应该知道Hadoop和Spark,Hadoop就是用java编写的,虽然Spark采用的是Scala语言,其也提供对java语言的支持。有一些情况下,例如,如果我们有大量原有的经过广泛测试的非 Java 代码,将它们全部用 Java 来重写,恐怕会带来巨大的工作量和长期的测试,在比方说如果我们的应用需要访问到特定的设备,甚至是仅符合公司内部信息交互规范的设备,或某个特定的操作系统才有的特性,这时Java 就显得有些力不从心了。为了应对类似的情况,Sun 公司在 JDK1.0 中就定义了 JNI(Java native interface) 规范,它规定了 Java 应用程序对本地方法的调用规则。
另一方面,使用JNI也是有代价的。因为JAVA程序运行在JVM之上,具有平台无关的特点。但是如果Java程序通过JNI调用了原生的代码则Java程序就丧失了平台无关性。另外 JNI 调用非常耗时,一次大概要花 0.5 ~ 1 个毫秒,所以应用JNI要根据实际需求权衡利弊,做出选择。
下面通过一个简单的实例进行介绍JNI的使用方法。
(1)首先我们需要创建一个java程序。
public class HelloJni{
static{
System.loadLibrary("hello");
}
public native void sayhello();
public static void main(String args[]){
HelloJni hello = new HelloJni();
hello.sayhello();
}
}
在这个程序中我们声明了一个本地方法sayhello()方法,并加载了名为hello的动态链接库。
(2)编译HelloJni
使用javac命令生成HellJni.class文件,这个过程会检验程序是否有语法错误。
javac HelloJni
(3)生成.h文件
使用javah命令生成HelloJni.h文件,我们要根据这个文件编写本地方法也就是hello.cpp文件,注意在hello.cpp文件中要包含这个头文件。
javah HelloJni
我们打开生成的这个.h文件,可以看到它里面已经声明好了我们需要实现的方法。其内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJni */
#ifndef _Included_HelloJni
#define _Included_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJni
* Method: sayhello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJni_sayhello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
(4)编写cpp文件
#include "HelloJni.h"
#include<iostream>
using namespace std;
JNIEXPORT void JNICALL Java_HelloJni_sayhello
(JNIEnv *, jobject)
{
cout << "Hello" << endl;
return;
}
再次强调在这个文件中要包含HellJni.h这个头文件。(5)生成动态链接库
生成动态链接库的命令另如下
g++ -fPIC -shared hello.cpp -o libhello.so -I$JAVA_HOME/include -I$JAVA_HOME/include/linux
-fPIC -shared表示生成动态链接库,如果对这个参数不了解可以参考我的linux系统下使用gcc/g++编译生成动态库这篇文章。
下面详细说一下-I参数,它表示所编译文件引入的头文件的查找目录,这里主要是指定jni.h的查找目录$JAVA_HOME/include,但是如我们所见hello.cpp文件中并没有包含这个头文件,只有HelloJni.h和iostream这两个头文件,原因是在HelloJni.h中包含了jni.h,而iostream在默认的查找目录中所以不要管。
头文件的查找顺序为-I指定,然后是系统默认的查找目录/usr/include和/usr/local/include,如果你的程序中引入的头文件不在这两个目录中,就需要特别指定。
还有一件特别重要的事是设定动态链接库的路径,否则会出现如下错误。设定动态链接库的路径的方法我在inux系统下使用gcc/g++编译生成动态库这篇文章里也有介绍,请参阅。
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at HelloJni.<clinit>(HelloJni.java:10)
(6)执行java程序
在命令行输入命令java HelloJni执行java程序。
参考
https://www.ibm.com/developerworks/cn/java/l-linux-jni/
https://www.ibm.com/developerworks/cn/java/l-linux-jni/