JNI是Java用来与C++之类的语言交互的一个工具,非常好用,配置不难。因为有时候一些函数是用c++库写的,没有Java版本,所以要使用很麻烦,但是如果Java可以调用C++的dll或者lib,那么就会很方便,JNI就是这样一种工具。下面介绍通俗易懂的配置方法:
Java代码——
首先,在Java里新建的类中写下需要被调用的C++函数,关键字是native,如图
GetJsonDwg是C++里实现的函数,这里需要被java调用,传送一个String类型的参数,返回一个String类型的结果。另外,需要些引用的路径,如图
TestJNI是dll或者lib的名称,也就是C++的工程名。
使用的时候就这样写,如图
这里的TestJNI表示的Java中类的名称(注意与上面的TestJNI表示的不是一个东西),我碰巧设成一样的了。hw.GetJsonDwg()就是应用了C++的方法。到此,Java部分代码就完成了。
cmd操作——
接下来需要对这个.java文件编译成C++可以引用的.h文件,我们可以在cmd中很容易地完成。首先在cmd中切换到Java工程的bin目录下,即.h所在的目录(用cd命令),如图
然后输入以下命令,如图:
这句话的意思是将编译的.h文件生成在当前路径中,头文件名是com_testjni_TestJNI,这个名称不是随便起的,com_testjni是Java类所在的包名,TestJNI是C++的库名。
这部执行完后,在bin目录下会生成一个 com_testjni_TestJNI.h的头文件,这就是上面命令行执行的结果。
C++部分——
接下来转到C++部分,我们创建一个Win32控制台应用程序,配置如图
我们给项目设置属性,C/C++--常规--附加包含目录,添加Java目录下的jni.h和jni_md.h所在的路径,我的电脑下的路径如图
一般位置基本一样,反正只要找到Java的目录,后面部分都是在include里找。
然后将之前编译的头文件com_testjni_TestJNI.h放到C++的工程目录下,就是放到和.cpp这些文件的同一个目录中,然后右键头文件--添加--现有项,将这个头文件引用进来。如果头文件有问题,就把#include <jni.h>改为#include "jni.h",反正我是不用改就可以用。
接下来创建一个.cpp,然后在里面写刚刚那个需要的函数,就是GetJsonDwg(),写法如下:
JNIEXPORT jstring JNICALL Java_com_testjni_TestJNI_GetJsonDwg (JNIEnv *env, jobject obj, jstring filepath){ jstring file = filepath; jstring a = env->NewStringUTF("LiYu"); return a; }
红色部分jstring对应Java中的String,绿色部分Java_com_testjni_TestJNI_GetJsonDwg的命名规则是:Java_工程名_函数名,黄色部分是默认的参数,自带,后面的jstring filepath就是传递的参数。以上仅是简单示例,重在告知原理,具体函数内容根据需求来实现。另外这个函数的写法可以到.h文件中去复制就行了!只不过自己要把出传递的参数添加一个具体的名称。
写好后右键项目--生成,然后在工程目录Debug中,我们就可以找到生成的.dll和.lib了。
配置Java Build Path——
右键包--Build Path--Configure Build Path,点击Libraries--JRE System Library--Native library location,点击Edit,把上面的.dll和.lib所在的路径添加进去,Apply and Close。如图
这个步骤是将库引用进来,这样代码中的System.loadLibrary("TestJNI");才能找到对应的库。
这样配置就完成了。
最后运行main函数,应该是可以正常调用C++的库中的函数了,如图
总结——
总结来说,JNI应该是一个不错的原生的接口,可以解决部分库缺乏Java版本的问题。不同的语言之间的交互确实是一个问题,这也包括传输的编码问题。这篇的文章没有深究传输编码问题,实际上如果C++直接返回一个中文字符串时会存在乱码的问题的,因为C++里是gbk编码,需要先转化为utf8/16才行,附代码如下:
//传递中文字符串,gb2312转utf8/16 jstring charTojstring(JNIEnv* env, char* str) { jstring rtn = 0; int slen = strlen(str); unsigned short * buffer = 0; if (slen == 0) rtn = (env)->NewStringUTF(str); else { int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, NULL, 0); buffer = (unsigned short *)malloc(length * 2 + 1); if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length) > 0) rtn = (env)->NewString((jchar*)buffer, length); } if (buffer) free(buffer); return rtn; }