我的JNI学习摘要(一) ---- JNI初体验

来源:互联网 发布:淘宝助理文件夹 编辑:程序博客网 时间:2024/04/28 23:04

声明:仅供自己学习!

初衷:假设有一个C函数,它能实现某个功能,因为某种原因,你不想费事使用Java重新实现它,即可以使用JNI。关键字native表示本地方法,native关键字提醒编译器该方法将在外部定义。方法没有直接跟着一个表示终结的分号,没有方法的主体,看上去类似于抽象方法的声明:

class HelloNative{    public static native void greeting();}
注意:本地方法也可以被声明为静态的,这里使用静态方法是因为我们不需要处理参数传递

此时,我们实际上可以编译这个类了,但虚拟机就会报UnsatisfiedLinkError异常,为了实现本地代码,需要编写一个相应的C函数

JNI的方法需要完全按照Java运行环境预期的那样来命名这个函数,规则如下:

1. 使用完整的Java方法名,比如:HelloNative.greeting。如果类属于某个包,那么还需要添加包名比如:com.horstmann.HelloNative.greeting

2. 用下划线替换掉所有的句号,并加上"Java_" 前缀如:Java_com_horstmann_HelloNative_greeting

如果涉及到重载本地方法的情况,方法名后附加两个下划线如:有一个本地方法greeting和另一个本地方法greeting(int repeat),那么第一个称为 Java_HelloNative_greeting__,第二个称为Java_HelloNative_greeting__I

但实际上是没有人会手工完成这些操作的,相反,应该运行javah(jdk/bin/javah.exe)它能够自动的生成函数名,要使用javah,首先需要编译源文件:

javac HelloNative.java

然后调用javah,从该类文件中产生一个C的头文件

javah HelloNative

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class HelloNative */#ifndef _Included_HelloNative#define _Included_HelloNative#ifdef __cplusplusextern "C" {#endif/* * Class:     HelloNative * Method:    greeting * Signature: ()V */JNIEXPORT void JNICALL Java_HelloNative_greeting  (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif

这个头文件包含了函数Java_HelloNative_greeting的声明(字符串JNIEXPORT 和 JNICALL 都是在jni.h定义的,他们问那些来自动态装载库的到处函数标明了依赖于编译器的说明符) 

这时只需要将函数原型从头文件复制到源文件中,并给出函数的实现代码-- HelloNative.c:

#include "HelloNative.h"#include <stdio.h>JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv * env, jclass cl){printf("Hello Native World!\n");}

我们当然也可以用C++来实现本地方法,但必须将本地方法声明为 extern "C"(阻止C++编译器生成C++特定的代码)

extern "C"JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv * env, jclass cl){cout << "Hello, NativeWorld!" << endl;}

将C代码编译到一个动态库中:例如在Linux 下的GNU C编译器,使用如下命令:

$ gcc -fPIC -I jdk/include -I jdk/include/linux -shared -o libHelloNative.so HelloNative.c

最后我们需要给程序添加一个对System.loadLibrary方法的调用---确保虚拟机在第一次使用该类前就会装载这个库,使用静态块,TestHelloNative.java:

class TestHelloNative{public static void main(String[] args){HelloNative.greeting();}static{System.loadLibrary("HelloNative");}}

javac TestHelloNative.java

java TestHelloNative

这里需要export 环境变量(Linux) 把当前路径添加到库路径中,通过设置LD_LIBRARY_PATH:

$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

或者设置java.library.path系统属性:

java -Djava.library.path=. TestHelloNative

总结: 遵循下面的步骤就可以将本地方法链接到java程序中:

1) 在java类中声明一个本地方法 ---native关键字

2) 运行javah一活的包含该方法的C声明的头文件

3) 用C实现该本地方法

4) 将代码置于共享类库中

5) 在java程序中加载该类库