背景
在很多时候 Java 调用动态库文件使用JNA是比 jni 体验更好,但有时候你会发现JNA也一样会出现错误的调用结果,这个时候我们需要C++底层语言去做调用动态库文件,其好处是非常高效且准确。但是C++语言怎么与java互通使其能通过java的语言调用C++里写的方法呢?
方法
答案是JNI,JNI是Java Native Interface的缩写,是java本地接口直通底层语言的纽带,他可以与C++完美的结合,下面让我们来看下如何结合。
步骤
我们把整个方法过程分如下步骤
- 首先编写java类,类名为DoorCardBt55Jni。注意 package ,这个package会决定你接下来java调用你后面采用C++生成对应的.dll文件的调用,如果换包将会调用不成功。
- 编译上步骤里的类文件,使其生成. class文件 。
- 通过javah指令生成C++的 头文件
- 通过上面的javac,javah指令可在对应的目录下生成如下C++头文件
- 我们可以大致看下这个头文件
- 然后将这个.h头文件加入到C++的开发工具中进行开发。新建一个BTFiveJava的C++的DLL项目,将头文件加入,并新建与生成的头文件同名的 源文件 .cpp。其中jni.h,jni_md.h是需要到java的jre的相关目录中获取。
- 开始开发源文件.cpp,这里主要是看从java到C++类型的过度,可以看到在最初声明java的方法XMS_Read_Guest_Card的时候是3个参数分别是int,String,String,生成头文件后变成jint,jstring,jstring,C++中的jint与java的int是一致的。定义的方法返回值是String[]数组,在C++中变成,jobjectArray,也是数组的意思。
- 通过编写代码生成BTFiveJava.dll文件
- 再回到java中,使用JNI方法来调用BTFiveJava.dll文件
知识点
上面步骤中最关键的知识点是.h文件中参数的传递和返回,我们来看下这个方法
JNIEXPORT jobjectArray JNICALL Java_com_foxhis_itf_doorcard_btff_DoorCardBt55Jni_XMS_1Read_1Guest_1Card
(JNIEnv * env, jobject obj, jint port, jstring hotelid, jstring hotelpwd)
JNIEXPORT :关键字,代表此修饰的方法为生成DLL的方法名
jobectArray:数组返回值,代表对应java的数组Strng[]
JNICALL:关键字,JNI调用触发
Java_com_foxhis_itf_doorcard_btff_DoorCardBt55Jni_XMS_1Read_1Guest_1Card:Java_java声明native方法的全名(.换成_)
JNIEnv * :代表java的JVM参数,可调用其中换算类型方法
jobject :代表当前java调用该方法的类,如上例子中的Test。
jint port, jstring hotelid, jstring hotelpwd:上面步骤中有介绍。
jstring 与 字符串 数组返回
在Java中,使用的字符串String对象是Unicoode(UTF-16)码,即每个字符不论是中文还是英文还是符号,一个字符总是占用两个字节。
但是在C/C++中一个字符是一个字节,但是C/C++中的宽字符是两个字节的。Java通过JNI接口可以将Java的字符串转换到C/C++的宽字符串(wchar_t*),或是传回一个UTF-8的字符串(char*)到C/C++,反过来,C/C++可以通过一个宽字符串,或是一个UTF-8编码的字符串创建一个Java端的String对象。
如:
const char* hID = env->GetStringUTFChars(hotelid, NULL);
const char* hPWD = env->GetStringUTFChars(hotelpwd,NULL);
通过GetStringUTFChars方法可将jstring(java中的string类型)转成C/C++中可用的字符串类型const char*(LPCSTR)。
而jint其实对应C++的是long类型,因为java中int始终是4个字节32位。而C/C++是根据操作系统来决定int类型的位数。
我们看看jni.h和jni_md.h头文件,可以更直观的了解:
typedef unsigned char jboolean;
typedef unsigned short jchar;
typedef short jshort;
typedef float jfloat;
typedef double jdouble;
typedef long jint;
typedef __int64 jlong;
typedef signed char jbyte;
最后介绍下如何创建字符串数组,NewObjectArray就是生成一个对象数组,它有三个参数,分别代表,数组长度,类型,初始化。
jobjectArray ret = env->NewObjectArray(jsize,env->FindClass(“Ljava/lang/String;”),NULL);通过这个方法创建jsize大小的对象空值数组。再通过循环遍历数组,再调用env->SetObjectArrayElement(ret, i, env->NewStringUTF(args[i].c_str()))将其值传入ret数组。
实现生成对应java的String[]数组。
如该篇文章对您有帮助记得收藏+转发,想学习更多知识记得关注哦,有疑问可下方留言,谢谢!