vc写了一个标准的动态库,有如下接口:
const WCHAR* execCommand(const char* param, int& errCode);
java中的接口声明:
// VideoUnit.java
public class VideoUnit
{
private VideoUnit() {}
static
{
System.load("videounit.dll");
}
public static native String execCommand(String strParam, int[] errCode);
}
此文件通过javac命令进行编译:
javac VideoUnit.java
生成 VideoUnit.class 文件,然后再通过javah命令生成.h文件供c++库使用:
javah VideoUnit
生成VideoUnit.h文件。
java中,对execCommand()的接口调用形式如下:
public static void main(String[] args) {
int[] execArr = new int[1];
System.out.println("execCommand: " + execCommand("", execArr));
System.out.println("errCode is " + execArr[0]);
}
注意,此函数可以直接写在VideoUnit类中,这样可以javac编译后,直接用java命令运行它。当然如此调试时,System.loada()的dll路径,必需是正确的。
在c++库中,为了java的调用,需要对接口execCommand()进行jni封装:
把 VideoUnit.h 文件加入进来,然后实现其接口 Java_execCommand(),如下的样子:
JNIEXPORT jstring JNICALSS Java_execCommand(JNIEnv* env, jclass obj, jstring strParam, jintArray errCode)
{
const char* params1 = env->GetStringUTFChars(strParam, JNI_FALSE);
jint* errCode1 = env->GetIntArrayElements(errCode, JNI_FALSE);
int errCode2 = 0;
const TCHAR* retv = execCommand(params1, errCode2);
*errCode1 = errCode2;
env->ReleaseIntArrayElements(errCode, errCode1, 0);
jstring retv1 = env->NewString((const jchar*)retv, _tcslen(retv));
return retv1;
}
这个函数的实现有一点绕,其总体思路就是:
把java的相关变量转换成本地可以使用的,然后调用c++对应的接口,结束后再把需要传出和返回的内容转换回java的类型。
其中的errCode参数,在C++接口中是引用,上述代码是调试时的过程,开始的时候在这个函数中添加打印回传的errCode是正确的,但在java代码中拿到的却是0,正常的应该不需要errCode2变量,execCommand()的第二个参数可以直接写成 (int&)errCode1[0]。为了调试,才添加了errCode2变量。
在调用完execCommand()之后,
env->ReleaseIntArrayElements(errCode, errCode1, 0);
一行非常重要,开始的时候就是缺少这一行导致errCode没有传回java代码。
此文的目的就是这一行。从此行大致可以看出,对于需要传出的参数,需要进行返回向转换操作,就像 ReleaseIntArrayElements()是反向转换int,其它还有很多,比如字符串。