【问题标题】:JNI: return an object from Java to C++, and pass it back to JavaJNI:从Java返回一个对象给C++,然后传回给Java
【发布时间】:2014-10-09 13:55:29
【问题描述】:

我有一些 Java 方法需要通过 JNI 从 C++ 调用。我的 JNI 实现基于
Is it possible to make a Java JNI which calls jdbc?

我的 Java 项目中有两个 java 文件。一个是定义一个类,另一个包含c++将调用的实际方法。

public class MyObject {
    private static int no;
    private static LocalDateTime time;
    private static String status;
    // getters, setters and toString
}

public class ObjectHandler {
    public static MyObject objectReturnToC;

    public static Object methodA (type1 arg1, type2 arg2, type3 arg3) {
        objectReturnToC = new MyObject();
        // setting fields in returnObject according to passed-in parameters
        return objectReturnToC;
    }
    public static void methodB(Object objectReturnedFromC) {
        // access fields in objectReturnedFromC, do computation and store in
    }
}

我在 Visual Studio 2010 中创建了 C++ DLL。有 JVM.cpp、JVM.h、JavaCalls.h 和 JavaCalls.cpp

JavaCalls.h

#ifdef JAVACALLSDLL_EXPORTS
#define JAVACALLSDLL_API __declspec(dllexport) 
#else
#define JAVACALLSDLL_API __declspec(dllimport) 
#endif

namespace JavaCalls
{
    class JavaCalls
    {
    public:
        static JAVACALLSDLL_API void *javaMethodA(type1, type2, type3);
        static JAVACALLSDLL_API string toString(void **javaObject);
        static JAVACALLSDLL_API void javaMethodB(void **javaObject);
    };
}

JavaCalls.cpp

namespace JavaCalls 
{
    void *JavaCalls::javaMethodA(type1 arg1, type2 arg2, type3 arg3) 
    {
        // invoke JVM
        // Find class, methodID
        jobject javaObject = CallStaticObjectMethod(jMain, "methodA",...);
        return javaObject;
    }
    void JavaCalls::javaMethodB(void** javaObject) {
        // invoke JVM
        // Find class, methodID
        CallStaticVoidMethod(jMain, "methodB",...);
    }
}

C++使用DLL调用Java方法A和方法B:

int main() 
{
    void* a = JavaCalls::JavaCalls::javaMethodA(arg1, arg2, arg3);
    // doing other stuff and updating fields in a
    JavaCalls::JavaCalls::javaMethodB(static_cast<void**>(a));
}

显然,传递指针,希望它对 C++ 可用是行不通的。但是我应该怎么做才能将 Java 对象保留在 C++ 中并稍后将其传递回 Java?我应该创建一个 C++ 结构并使用 GetObjectField 将 Java 对象字段映射到其中吗?

【问题讨论】:

  • 你为什么使用void指针?是什么阻止你只是通过jobjects?使用void** 有什么意义?
  • 我认为 void 指针是用于通用指针的,不知道我可以在 c++ dll 中保留 jobject 并传递它。我一定要试试。稍后我还需要移植它,以便 C 和 FORTRAN 可以调用 Java 方法。即使使用 C 和 FORTRAN,您认为通过 jobject 是一个不错的选择吗?
  • 问题是为什么需要这样调用jdbc?您可以直接从 C++ 连接到 DB,它可能会更高效。
  • 我知道我可以直接从 c++ 连接 DB,但我们决定不需要在所有用户计算机上安装 obdc 驱动程序(这引起了很多头痛和开销,请不要问我如何.) 这就是为什么我需要在 C++ 中通过 JNI 调用 jdbc

标签: java c++ pointers java-native-interface


【解决方案1】:

我不太明白为什么您的代码中需要void**。如果你想让界面不透明,只需使用void*。另外不要忘记在返回的jobject 上调用NewGlobalRef()DeleteGlobalRef() - 这将防止垃圾收集器破坏它:

void *JavaCalls::javaMethodA(type1 arg1, type2 arg2, type3 arg3) 
{
    jobject javaObject = CallStaticObjectMethod(jMain, "methodA",...);
    return NewGlobalRef(jMain, javaObject);
}

void JavaCalls::javaMethodB(void* javaObject) {
     CallStaticVoidMethod(jMain, "methodB", static_cast<jobject>(javaObject));
}

// add this method - it should be called when you finish using the object
void JavaCalls::ReleaseObject(void* javaObject) {
     DeleteGlobalRef(jMain, static_cast<jobject>(javaObject));
}

【讨论】:

  • 我可以在 JavaMethodB 的末尾添加 DeleteGlobalRef() 吗?
  • @wolf97084 是的,当然可以,如果您不再需要该对象。我不知道你想调用多少次JavaMethodB或者其他方法,所以我写了一个通用的实现。
  • 我将所有 void** 更新为 void*,现在尝试在从 methodA 返回的作业对象上调用 toString。但在 C++ 调试器中,从 toString() 返回的字符串始终为 0x00000000。有没有一种特殊的方法可以调用 toString()?我使用了 jmethodID jToString = env->GetMethodIS(jMainObject, "toString", "()Ljava/lang/String;"); jstring jObectString = (jstring) env->CallObjectMethod(objectreturnedFromMethodA, jToString);
  • @wolf97084 我认为你最好提出一个不同的问题。目前我不知道如何解决这个问题。您可以尝试覆盖您班级的toString() 并在其中放置一个断点/登录以确保它被调用。
【解决方案2】:

有一个很棒的助手程序叫做javah.exe

按照您喜欢的方式编写代码并将javah 执行到.class。它将为您创建一个完全兼容的.h 文件。

你会因此而成为:

public class Test {

    public class MyObject {
        private int no;
        private String status;
    }

    public class Callback {
        public void callback(MyObject afterCpp) {

        }
    }

    public native void register(Callback v, MyObject beforeCpp);
}

这个标题:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Test */

#ifndef _Included_Test
#define _Included_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Test
 * Method:    register
 * Signature: (LTest/Callback;LTest/MyObject;)V
 */
JNIEXPORT void JNICALL Java_Test_register
  (JNIEnv *, jobject, jobject, jobject);

#ifdef __cplusplus
}
#endif
#endif

您只需将callback 调用到第三个 c++ 参数即可。

【讨论】:

  • 我以为这是通过JNI从Java调用C++?猜我错了。 (我对 JNI 很陌生。)我不认为我传递给 C++ 的 Java 对象保存在内存中(它有地址,但所有数据都不在那里。)我是将 jobject 传递给 C++ 还是只是用你的 void 指针解决方案?
  • Java 无法理解来自cc++ 的指针。 Java 是跨平台的,而 c++32bit64bit。 JNI 是一个移植接口,而不是设计接口。这就是为什么c++-Types 与java-Types 完全不同的原因:您需要翻译它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-03
  • 1970-01-01
  • 2013-08-07
  • 1970-01-01
  • 2014-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多