本篇是该系列的最后一篇文章,就让我们以JNI作为结束吧!
众所周知,使用多种语言协同开发时,经常会导致一些未知问题的发生,也会给我们程序的调试带来一定的负担。鉴于此,我们也只有在充分了解到必要性之后再决定用C/C++来替换部分Java代码,以达到我们预期的目的,考虑使用本地代码主要有以下三个理由:
1) 你的应用需要访问系统的各个特性和设备,这些特性和设备通过Java平台是无法访问的;
2) 你已经有了大量的测试过和调试过得用另一种语言编写的代码,并且知道如何将其导出到所有的目标平台上;
3) 通过基准测试,你已经发现所编写的Java代码比用其他语言编写的等价代码要慢得多。
在Java中提供了用于和本地C语言代码进行互操作的API,称为Java本地接口(JNI)。
1. 第一个本地方法:
1) 声明一个含有本地方法的类:
1 public class HelloNative {
2 public static native void greeting();
3 }
注意以上代码中的native关键字,以及在函数声明的末尾直接用分号结束该函数的声明。
2) 将该.java文件编译成.class文件,如果使用eclipse直接用IDE编译即可,否则可以使用命令行:
javac HelloNative.java
将会发现在当前目录下生成了相应的HelloNative.class文件。
3) 通过%JAVA_HOME%/bin/javah命令在命令行窗口为HelloNative.class文件生成相应的C语言使用的头文件。
javah HelloNative #这里不要添加.class后缀,执行目录为HelloNative.class所在的目录。
需要注意的是,在我们的例子中并没有给HelloNative方法添加包名,如:
1 package mytest;
2 public class HelloNative {
3 public static native void greeting();
4 }
针对该种情况在基于.class文件生成C语言要求的头文件时,需要做出以下调整:
javah mytest.HelloNative #这里在类的前面添加了包名mytest,包名和类名之间使用dot作为连接符。还需要注意的是执行当前命令的目录为HelloNative.class所在目录的上一级目录,如果包的名字为package mytest.first; 则执行目录为HelloNative.class的上两级目录,以此类推。执行成功后将会在当前执行目录下生成一个名为mytest_HelloNative.h(针对包名为mytest的case)。该文件中包含了和HelloNative.greeting()对应的C语言导出函数的声明。为了实现本地代码,要求生成的C函数名符合Java在JNI中定义的命名规则,如:
1) 使用完整的Java类名.方法名,如:HelloNative.greeting。如果类属于某个包,那么在前面添加包名,如mytest.HelloNative.greeting;
2) 用下划线替换掉所有的dot,并加上Java_前缀,如:Java_mytest_HelloNative_greeting;
3) 如果你重载本地方法,既使用相同名字提供多个本地方法,那么必须在名称后附加两个下划线,后面再加上已编码的参数类型。如:Java_mytest_HelloNative_greeting__表示当前没有参数的greeting,如果在Java中同时定义了另一个greeting(int repeat),其对应的C函数声明为Java_mytest_HelloNative_greeting__I,这里的I表示参数类型,更为具体的描述,后面会逐步给出。
以下为该头文件的代码:
1 /* DO NOT EDIT THIS FILE - it is machine generated */
2 #include <jni.h>
3 /* Header for class mytest_HelloNative */
4 #ifndef _Included_mytest_HelloNative
5 #define _Included_mytest_HelloNative
6 #ifdef __cplusplus
7 extern \"C\" {
8 #endif
9 /*
10 * Class: mytest_HelloNative
11 * Method: greeting
12 * Signature: ()V
13 */
14 JNIEXPORT void JNICALL Java_mytest_HelloNative_greeting
15 (JNIEnv *, jclass);
16
17 #ifdef __cplusplus
18 }
19 #endif
20 #endif
我们后面需要做的是为该头文件创建一个对应的HelloNative.c文件包含导出函数的实现代码,如:
1 #include \"HelloNative.h\"
2 #include <stdio.h>
3 JNIEXPORT void JNICALL Java_mytest_HelloNative_greeting(JNIEnv* env, jclass cl) {
4 printf(\"Hello Native World!n\");
5 }
注意如果使用了C++的代码,由于Java并未提供和C++的交互能力,因此需要在该函数前加extern \"C\"来表示按照C语言的方法导出,以阻止编译器生成C++特有的代码,如:
1 extern \"C\"
2 JNIEXPORT void JNICALL Java_mytest_HelloNative_greeting(JNIEnv*