JNI不完全指南
1. 概述
JNI做为对JVM的补充,可以完成一些游离于JVM之外的代码,完成一些OS严重依赖的功能,比如你想自己实现基于IMCP的ping(如果问为什么,那么请重新学习一下IP/TCP/Socket)。
JNI包括Java代码和Native代码,所谓的native代码并非说一定是C的,即使你用其它的语言实现也可以,但是需要把它包装成操作系统特定的库,JRE中会有专门的代码去利用操作系统接口去动态加载它。
实现JNI的主体步骤如下:
1) 完成Java代码
2) 完成native代码
3) 封装native代码到系统库
4) 使用native库
2. Step By Step样例
我们一步一步来实现一个基于Sun JDK的JNI的例子。其中,native代码用C来实现。而最终,这个JNI的例子将分别被部署到windows和linux上,两者有小小的区别将在后面说明。
l Java
在需要使用native方法的类中,加上native的声明,然后像abstract方法一样不必给出实现。
package demo;
public class HelloWorld {
final static String name = "laurence";
public static String getName(){
return name;
}
public native String sayHello(String msg);
}
很普通的Java类,注意其中的阴影部分。
l C
分为两部分
1) 生成native代码的头文件
2) 实现native代码
生成native头文件
javah -classpath target/classes -d . demo.HelloWorld
target/classes是指定当前目录下的target/classes目录为classpath,demo.HelloWorld的class文件就在这下面
-d指输出头文件的目录,这里设为当前了
demo.HelloWorld是包含了native方法声明的Java类名
完成javah命令之后,会输出一个头文件,在本例中是demo_HelloWorld.h,代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class demo_HelloWorld */
#ifndef _Included_demo_HelloWorld
#define _Included_demo_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: demo_HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_demo_HelloWorld_sayHello
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
实现native代码
用C实现demo_HelloWorld.c,代码如下:
#include "demo_HelloWorld.h"
#include <time.h>
JNIEXPORT jstring JNICALL Java_demo_HelloWorld_sayHello(JNIEnv* env,
jobject method, jstring param) {
char buf[1025];
const char *pt = (*env)->GetStringUTFChars(env, param, NULL);
time_t now;
time(&now);
strcpy(buf, pt);
strcat(buf, asctime(localtime(&now)));
return (*env)->NewStringUTF(env, buf);
}
这个方法做的事情就是读取参数param,在后面加上个时间戳并返回新的字符串。
简单解释一下其中的关键行代码。
GetStringUTFChars是jre中提供的jni方法,用来得到java string的字符串。
NewStringUTF是jre中提供的jni方法,用来新建java string。
具体的jni方法,请参考sun的jni帮助文档和样例。
l 操作系统相关
对于不同的操作系统,都有系统相关的库封装方式。在Linux上是Shared Object(.so文件),在Windows上就是Dynamic Load Library(.dll文件),其它不常用的请自行解决吧。
Linux
不需要额外的工作,直接编译成Shared Object即可。
一条指令全搞定:
gcc -Wall -m32 -fPIC -I/usr/java5/include -I/usr/java5/include/linux -Wl,-melf_i386 -shared -lc -Wl,--soname=hello