1 引言
1.1 简介
在实际项目中,很多时候是以多种语言混合开发的。常用的模式是使用 C/C++ 做底层支撑,然后上层辅之以高级语言。 JNI可以使 native 代码与 Java 对象交互,而不会像 Java 代码中的功能那样受到诸多限制, 往往充作 Java 与 C/C++/Assembly 语言的粘合剂。
JNI 明确分开了 Java 代码与 native 代码的执行,为两者的通信定义了一套清晰的API.避免 native 代码对 JVM 的直接内存引用。
2 JNI 入门
2.1 JNI 调用 C
2.1.1 java 中的 native 方法声明
HelloJNI.java
1 2 3 4 5 6 7 8 9 10 11 |
public class HelloJNI{ static{ System.loadLibrary("hello"); //运行时加载本地库 hello.dll(Windows)或libhello.so(Unixes) 或 libhello.dylib(MacOX) } private native void sayHello(); //声明 native 方法 public static void main(String[] args ){ new HelloJNI().sayHello(); //调用本地方法 } } |
当HelloJNI类加载的时候,静态初始化调用 System.loadLibrary() 来加载本地库 hello.dll(或libhello.so)。如果不能加载 hello 库,会抛出 UnsatisfiedLinkError 异常。
然后我们使用 native 关键字声明了 native 方法 sayHello() ,这表明该方法是使用其它语言实现的。
最后 main() 方法实例化了 HelloJNI 并调用了 sayHello().
然后我们编译这个类:
1 |
> javac HelloJNI.java |
2.1.2 生成 C/C++ 头文件
使用 javah 创建头文件:
1 |
> javah HelloJNI |
会得到如下 .h 文件 HelloJNI.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* 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 |
这份自动生成的头文件里声明一个 C 函数 Java_HelloJNI_sayHello:
1 |
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject); |
这个方法名是由javah转换来的,转换规则为:
1 |
Java_{package_and_classname}_{function_name}(JNI arguments) |
包名中的 点 会被转换为 下划线
函数中带有两个参数(虽然我们在本例中并没有使用这两个参数)
- JNIEnv*: 指向 JNI environment ,提供对 JNI 方法的访问
- jobject: 指向 this Java 对象
2.1.3 C 代码的实现
HelloJNI.c
1 2 3 4 5 6 7 8 |
#include <jni.h> #include <stdio.h> #include "HelloJNI.h" JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv * env, jobject obj) { printf("Hello World!"); } |
这个实现将在控制台打印万能的 "Hello World!"
文件中包含了 jni.h 头文件。这个头文件由 java 提供,位于 %JAVA_HOME%/include 路径下(Windows下还需要引用 %JAVA_HOME%/include/win32).
2.1.4 编译 C 库
接下来,编译这个 c 文件:
1 2 3 4 5 6 |
#Windows gcc > gcc -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o hello.dll HelloJNI.c #Windows VS cl.exe link.exe > cl /c /I%JAVA_HOME%/include /I%JAVA_HOME%/include/win32 HelloJNI.c > link HelloJNI.obj /DLL /OUT:hello.dll |