您的位置:首页 > 编程语言 > C语言/C++

C++ 与 JAVA调用问题

2016-04-21 15:23 288 查看



c++调用java其实并不复杂,分为几个步骤:

在说调用之前,我们先来看看我们需要调用的java类

public class Test {

Java代码



public Test() {

}

public String getMessage(){

return "test ok";

}

public TestObject getObject() {

System.out.println("invoke getObject ok***");

TestObject to = new TestObject();

to.setName("name");

to.setPwd("pwd");

return to;

}

public void test() {

System.out.println("%%% invoke test ok***");

}

这是一个很简单的类,他有一个无参的构造函数,有三个方法,一个带有String返回值的方法,一个是返回一个我们自定义的对象TestObject,另外还有一个没有返回值的test方法。

接下来是TestObject类

Java代码



public class TestObject {

public String name;

public String pwd;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getPwd() {

return pwd;

}

public void setPwd(String pwd) {

this.pwd = pwd;

}

public void callback(String content) {

System.out.println("%%%call back ok*** cotnent is: " + content);

System.out.println("name is: " + name);

System.out.println("pwd is: " + pwd);

}

}

也是一个很简单的类,有两个属性,还有一个回调函数,主要是想演示一下我们如在C++中获取该对象以后,进行回调,这也是我们经常会遇到的问题。

上面看了我们的java测试类,接下来,我们来具体看看如何进行调用

创建jvm

我们首先需要初始化一些jvm的参数,然后创建出jenv和jvm以便我们之后的调用

Cpp代码



JavaVMOption options[1];

JNIEnv *env;

JavaVM *jvm;

JavaVMInitArgs vm_args;

long status;

jclass testCls;

jmethodID testMid;

jobject testJobj;

jobject tookitReturnObj;

//设置Java类的路径

options[0].optionString = "-Djava.class.path=. ;D:\\workspace\\my\\JniTest\\jnitest.jar";

vm_args.version = JNI_VERSION_1_6;

vm_args.nOptions = 1;

vm_args.options = options;

vm_args.ignoreUnrecognized = JNI_TRUE;

status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

这里需要指出的是,在设置classpath的时候,如果需要引用的是jar包,需要把jar包的名称一并引入,而不能是只指定jar所在目录,这就会造成一个问题,如果我们依赖的jar很多,那么classPath就需要逐一写出jar包名称来,这对于有着几十个引用jar的项目来说还是会造成一定的麻烦,我推荐大家使用fatjar的一个工具,fatjar可以把依赖的jar合并打成一个jar包,这样的话,可以减少我们在书写classpath的过程中,写太多的jar包,为我们省点事,而且对于维护来讲也比较清晰。

加载调用的java类

当我们创建完jvm后,会得到一个返回值,如果为0说明创建成功,如果否则创建失败。

当我们成功创建完jvm后,接下来我们找到需要调用的java类

Cpp代码



if (status != JNI_ERR)

{

testCls = env->FindClass("jni/test/Test");

}

这里我们要使用Test的一个java类,它所在的包尾jni.test。

如果加载成功,那么testCls不为0,否则将返回0

调用构造函数,创建对象

像我们在java语言中一样,我们首先需要调用构造函数创建一个对象实例,然后再进行方法调用,那么在C++中同样需要我们这么做,当然调用静态方法例外。

testMid = env->GetMethodID( testCls, "<init>", "()V");

Java代码



if (testMid != 0)

{

testJobj = env->NewObject(testCls,testMid);

std::cout << "init test class ok" << std::endl;

}

我们可以看到我们首先进行GetMethodID调用,这个函数有三个参数,一个是我们之前获取的jclass对象,第二参数是需要调用的java类中的方法名称,由于我们是调用构造函数,所以<init>是个固定的写法,他就是说明我们要调用构造函数,第三参数是调用方法的参数类型,关于这个参数的类型,我们可以使用javap命令来获取,具体命令如下:

javap -p -s jni.test.Test

然后我们会得到这样的信息

Java代码



Compiled from "Test.java"

public class jni.test.Test extends java.lang.Object{

public jni.test.Test();

Signature: ()V

public java.lang.String getMessage();

Signature: ()Ljava/lang/String;

public jni.test.domain.TestObject getObject();

Signature: ()Ljni/test/domain/TestObject;

public void test();

Signature: ()V

}

我们可以看到Test()构造函数,在Signature后面就是该方法的参数类型。

当我们获取构造函数以后,我们就可以对其进行实例化了,使用NewObject

方法调用

接下来我们就可以进行方法调用了

Cpp代码



testMid = env->GetMethodID( testCls, "test","()V");

if (testMid != 0)

{

env->CallVoidMethod(testJobj,testMid);

}

testMid = env->GetMethodID( testCls, "getMessage","()Ljava/lang/String;");

if (testMid != 0)

{

jobject msg = env->CallObjectMethod(testJobj,testMid);

std::cout << msg << std::endl;

printf("msg : %d",msg);

}

上面的代码里我们调用两个方法,分别是test和getMessage,我们都是先调用GetmethodID来获取一个jmethodID对象,然后调用CallXXXMethod来进行调用,跟java中的反射调用是一样的。

如果我们需要调用静态方法,我们只需先GetStaticMethodID然后调用CallStaticXXXMethod即可,jni.h中定义了很多Call的方法,比如CallIntMethod,CallBooleanMethod等等,有兴趣可以去查看jni.h

下面我们来说一下如何进行回调

我们的java测试类Test中有一个getObject的测试方法,得到了一个java对象,我们拿到该独享后如何进行下一步的调用呢,其实很简单,跟咱们之前的例子是类似的

Cpp代码



testMid = env->GetMethodID( testCls, "getObject","()Ljni/test/domain/TestObject;");

if (testMid != 0)

{

jobject obj = env->CallObjectMethod(testJobj,testMid);

jobject listener = env->NewGlobalRef(obj);

jclass clsj = env->GetObjectClass(listener);

jmethodID func = env->GetMethodID(clsj, "callback", "(Ljava/lang/String;)V");

if(func != NULL)

{

jobject msg1 = env->CallObjectMethod(testJobj,testMid);

env->CallVoidMethod(listener, func, msg1);

}

std::cout << obj << std::endl;

printf("obj : %d",obj);

}

同样我们通过GetMethodID获取getObject方法,然后调用该方法,得到一个返回的对象obj,我在代码里进行一下处理,创建了一个全局引用,如果大家只想把他当做局部变量使用,那么不创建全局引用也可以;然后通过GetObjectClass我们得到了jclass对象,接下来就跟我们之前的过程一样了,获取方法,进行调用即可。

上面的代码中我在调用callback方法前有调用了一次getObject方法,主要是测试一下,获取的对象,在进行callback的时候能不能保持其状态。经验证是可以的。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: