JNI是Java与C、C++、Objective-C、Objective-C++等静态编译语言以及汇编语言相交互的接口。尽管目前而言,Java提供了诸多运行时性能较高的运行时库,但是在很多方面,尤其是高性能计算领域,Java提供的高效库还不是很多,因此我们可以通过JNI接口将我们用静态语言以及汇编编译连接为动态库后给Java应用程序加载调用。
首先,Java为不同的操作系统平台提供了各自相适应的运行时环境以及根据不同的编译器提供了JNI头文件。JNI头文件一般由两个组成:jni_md.h提供了依赖于平台的头文件;jni.h提供了jni所需要的接口声明以及各种类型的定义。这两个头文件都可以在JDK的include中找到。我们在创建一个JNI动态库的工程时应该将工程的输出目标设置为动态连接库(瘟抖死下为.dll,Unix-like系统下为.so,OS X下为.dylib)。我们在创建工程时可以将这两个头文件导入到工程中。
然后我们可以看以下代码:
然后我们可以看以下代码:
#include <stdio.h>
#include "jni.h"
JNIEXPORT jint JNICALL Java_MyJNI_myTest(JNIEnv *env, jobject obj, jintArray dstArray, jintArray srcArray)
{
jboolean isCopy = 0;
jint* csrcArray = (*(*env)->GetIntArrayElements)(env, srcArray, &isCopy);
jsize dstSize = (*(*env)->GetArrayLength)(env, dstArray);
jsize srcSize = (*(*env)->GetArrayLength)(env, srcArray);
jsize length = dstSize >= srcSize? dstSize : srcSize;
printf("The length is: %u\n", length);
printf("Is copy available? %d\n", isCopy);
printf("The sum of source array is: %d", csrcArray[0] + csrcArray[1]);
jint dstBuffer[32];
for(jsize i = 0; i < length; i++)
dstBuffer[i] = csrcArray[i] + i + 100;
(*(*env)->SetIntArrayRegion)(env, dstArray, 0, length, dstBuffer);
return 100;
}
以上代码提供了一个导出Java方法,Java_后面的名字表示在Java应用端的类名,类名后面的_所跟的名字是该类的方法名(该方法可以是成员方法也可以是类方法)。一个标准的JNI方法应该提供两个形参,一个是JNIEnv*,另一个是jobject。env提供了Java运行时环境的句柄,后面调用各种Java运行时方法都需要传这个参数。jobject是指向该对象的指针,相当于Java应用端中的this。因此如果你所定义的这个JNI方法在Java应用端是一个类方法,那么这个参数即被忽略。
以上方法实现的功能是将第二个数组的每个元素加上100再加其索引后的值相应赋值给第一个目的数组元素。这边假定两个数组的元素个数最多为32。最后返回一个int类型的值100。
然后,我们可以看Java端相应的代码:
class MyJNI {
native static int myTest(int[] dstArray, int[] srcArray);
}
public class Test {
static {
System.loadLibrary("MyJNI");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] dstArray = new int[2];
int[] srcArray = {100, 600};
System.out.println("The value is: " + MyJNI.myTest(dstArray, srcArray));
System.out.println("The sum is: " + (dstArray[0] + dstArray[1]));
}
}
这里调用System.loadLibrary方法来加载动态连接库。
在使用Linux环境时必须注意,在运行前必须设置LD_LIBRARY_PATH环境变量以指定动态库所在的路径,比如:
LD_LIBRARY_PATH='/usr/home/java_test'
export LD_LIBRARY_PATH
如果使用Eclipse开发环境的话,我们可以在当前项目的Run Configuration以及Debug Configuration中来设置此环境变量。方便起见,我们在设置路径时可以使用右侧的variable按钮,选择project_loc。这个内建变量指定了当前项目的系统绝对路径。然后把动态库导入到当前项目的根目录下即可。
最后,我们举一个更为完整的例子。这个例子中,我们在JNI代码中将加入对汇编语言的一起编译链接,做成.so文件。这里要注意的是,由于汇编器不支持-fPIC选项,因此要与汇编文件一起链接的话就不能带-fPIC的编译选项了,否则连接就会失败。
我们先看Java代码:
package test;
public class Main {
static {
System.loadLibrary(("ctest"));
}
native static int myJNITest();
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("The answer is: " + myJNITest());
}
}
这里加入了一个package——test,因此,对于JNI的native函数myJNITest而言,其前缀就需要把包名加上,变为——
Java_test_Main_myJNITest。