C++ 与 JAVA调用问题

来源:互联网 发布:剑三苍云成男脸型数据 编辑:程序博客网 时间:2024/03/29 18:34

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

 

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

public class Test {

Java代码  收藏代码
  1. public Test() {  
  2.       
  3. }  
  4.   
  5. public String getMessage(){  
  6.     return "test ok";  
  7. }  
  8.   
  9. public TestObject getObject() {  
  10.     System.out.println("invoke getObject ok***");  
  11.     TestObject to = new TestObject();  
  12.     to.setName("name");  
  13.     to.setPwd("pwd");  
  14.     return to;  
  15. }  
  16.   
  17. public void test() {  
  18.     System.out.println("%%% invoke test ok***");  
  19. }  

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

接下来是TestObject类

 

Java代码  收藏代码
  1. public class TestObject {  
  2.     public String name;  
  3.       
  4.     public String pwd;  
  5.   
  6.     public String getName() {  
  7.         return name;  
  8.     }  
  9.   
  10.     public void setName(String name) {  
  11.         this.name = name;  
  12.     }  
  13.   
  14.     public String getPwd() {  
  15.         return pwd;  
  16.     }  
  17.   
  18.     public void setPwd(String pwd) {  
  19.         this.pwd = pwd;  
  20.     }  
  21.       
  22.     public void callback(String content) {  
  23.         System.out.println("%%%call back ok*** cotnent is: " + content);  
  24.         System.out.println("name is: " + name);  
  25.         System.out.println("pwd is: " + pwd);  
  26.     }  
  27.       
  28. }  

 

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

 

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

 

创建jvm

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

 

Cpp代码  收藏代码
  1.        JavaVMOption options[1];  
  2. JNIEnv *env;  
  3. JavaVM *jvm;  
  4. JavaVMInitArgs vm_args;  
  5. long status;  
  6. jclass testCls;  
  7. jmethodID testMid;  
  8. jobject testJobj;     
  9. jobject tookitReturnObj;  
  10. //设置Java类的路径  
  11. options[0].optionString = "-Djava.class.path=. ;D:\\workspace\\my\\JniTest\\jnitest.jar";  
  12. vm_args.version = JNI_VERSION_1_6;  
  13.        vm_args.nOptions = 1;  
  14.        vm_args.options = options;  
  15.        vm_args.ignoreUnrecognized = JNI_TRUE;  
  16.        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代码  收藏代码
  1. if (status != JNI_ERR)  
  2. {  
  3.     testCls = env->FindClass("jni/test/Test");  
  4. }  

 

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

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

 

调用构造函数,创建对象

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

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

Java代码  收藏代码
  1. if (testMid != 0)  
  2. {  
  3.     testJobj = env->NewObject(testCls,testMid);  
  4.     std::cout << "init test class ok" << std::endl;  
  5. }  

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

javap -p -s jni.test.Test

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

 

Java代码  收藏代码
  1. Compiled from "Test.java"  
  2. public class jni.test.Test extends java.lang.Object{  
  3. public jni.test.Test();  
  4.   Signature: ()V  
  5. public java.lang.String getMessage();  
  6.   Signature: ()Ljava/lang/String;  
  7. public jni.test.domain.TestObject getObject();  
  8.   Signature: ()Ljni/test/domain/TestObject;  
  9. public void test();  
  10.   Signature: ()V  
  11. }  

 

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

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

 

方法调用

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

Cpp代码  收藏代码
  1. testMid = env->GetMethodID( testCls, "test","()V");  
  2. if (testMid != 0)  
  3. {  
  4.     env->CallVoidMethod(testJobj,testMid);             
  5. }  
  6.   
  7. testMid = env->GetMethodID( testCls, "getMessage","()Ljava/lang/String;");  
  8. if (testMid != 0)  
  9. {  
  10.     jobject msg = env->CallObjectMethod(testJobj,testMid);  
  11.     std::cout << msg << std::endl;  
  12.     printf("msg : %d",msg);  
  13. }  

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

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

 

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

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

Cpp代码  收藏代码
  1. testMid = env->GetMethodID( testCls, "getObject","()Ljni/test/domain/TestObject;");  
  2.         if (testMid != 0)  
  3.         {  
  4.             jobject obj = env->CallObjectMethod(testJobj,testMid);  
  5.             jobject listener = env->NewGlobalRef(obj);  
  6.             jclass clsj = env->GetObjectClass(listener);  
  7.             jmethodID func = env->GetMethodID(clsj, "callback", "(Ljava/lang/String;)V");  
  8.             if(func != NULL)   
  9.             {  
  10.                 jobject msg1 = env->CallObjectMethod(testJobj,testMid);  
  11.                 env->CallVoidMethod(listener, func, msg1);  
  12.             }  
  13.             std::cout << obj << std::endl;  
  14.             printf("obj : %d",obj);  
  15.         }  
 

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

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

原创粉丝点击