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

如何在c++中调用java代码

2016-07-31 23:40 453 查看


在我们的日常工作中,可能会遇到不同语言之间相互调用的问题,常见的有java调用C/C++或者在C/C++中调用java,我们可以基于sun提供的jni技术来实现这两种语言之间的相互调用,这篇文章来说一下在c++中调用java的情况,至于java如何调用c我会在另外一篇文章中单独讲。

 

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的时候能不能保持其状态。经验证是可以的。

转载地址:http://www.blogjava.net/rabbit/archive/2012/06/07/380193.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: