最近把公司项目的一个消息插件换成JAVA,要在C++层调用java,在java里面会有自身的回调,然后我想在这个回调里面调回到我的c++代码里面。
之前做过C++到C#之间的调用,拿一套语法很复杂诡异,我以为C++和JAVA之间的互相调用也很复杂,等我搞定之后,发现,简直方便快捷。
下面说一下步骤。
1,我用的是VS2010,安装了JDK1.8。
2,要写一个JAVA类,如下。
package test;
public class Test{
static{
System.loadLibrary("testJni"); //这里是导入库,这个库就是本地方法的实现库文件。
}
public static void main(){
System.out.println(hello()); //这个方法是JAVA类的自身成员,里面调用了本地方法。
}
public static native String hello();//这是申明本地方法,即在JNI环境下执行的代码。
}
3,
先用javac test.java(上面代码的源文件名称),这时候会生成test.class。这个class文件,到时候会在vs2010里面的工程用到。如果不知道函数签名,最好是用 javap -s -p test来看看函数的签名。
4,
用 javah -jni test 这个时候会生成一个 test_Test.h 的头文件,这个文件里面申明了本地方法,需要我们在库中去实现方法。如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class test_Test */
#ifndef _Included_test_Test
#define _Included_test_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: test_Test
* Method: hello
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_test_Test_hello(JNIEnv *, jclass); //这就是本地代码在头文件中的申明
//extern "C" __declspec(dllexport) void helloC();
void helloC();
#ifdef __cplusplus
}
#endif
#endif
5,
用VS2010新建一个DLL工程,配置好JAVA的环境变量,导入JAVA依赖库。导入test_Test.h 到工程,并新建一个test_Test.cpp,然后CPP代码如下
#include "stdafx.h"
#include "test_Test.h"
JNIEXPORT jstring JNICALL Java_test_Test_hello(JNIEnv * env, jclass obj, )
{
int a = 0;
a ++;
return env->NewStringUTF("world");
}
然后编译之后,testJni.dll 和 testJni.lib,只需要DLL文件就可以。
6,
VS2010新建一个工程,导入JAVA的环境。下面解说一下C++中如何调用JAVA的类,
JavaVMInitArgs vm_Args;
JavaVMOption options[3];
JavaVM * jvm = NULL;
JNIEnv * env = NULL;
jmethodID mid = NULL;
options[0].optionString = "-Djava.class.path=E:\\JAVA\\project"; //这个是你的JVM启动的时候,它去找类的时候的根目录。如果按着我写的测试,那么这个路径就要写成你生成的class的目录。还要说一点就是,我的JAVA代码中,是有个test包名的,所以这个路径要在包名那个级别的目录。
options[1].optionString = "-Djava.compiler=NONE";
options[2].optionString = "-verbose:NONE";
vm_Args.version = JNI_VERSION_1_8;
vm_Args.nOptions = 1;
vm_Args.options = options;
vm_Args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_Args); //启动JAVA虚拟机
jclass clstest = env->FindClass("test/Test"); //在test这个包下面,找Test这个类,
jmethodID mid = env->GetStaticMethodID(clstest, "main", "()V"); //这是找这个类的静态方法,后面那个“()V” ,是方法的签名。
env->CallStaticVoidMethod(cls, mid); //这里是调用。
这时候你会在控制台看到“world”的打印输出
总结一下,其实,C#和C++交互,JAVA和C++交互大致相同。都需要一个中间层来做过渡。在本文中,那个DLL就是中间层,用来把C++和 java中的数据做交换。