【问题标题】:keep some sort of c++ object alive over multiple jni calls通过多个 jni 调用保持某种 c++ 对象处于活动状态
【发布时间】:2011-12-06 09:06:49
【问题描述】:

我的 java 代码将调用现有的 c++ 代码来解析文件。它将生成一个保存许多数据的对象。 我将调用jni的第二种方法来访问这些数据, 当我调用第二种方法时,我必须再次解析文件。这显然是正确的行为。

有没有办法解决这个问题? 顺便说一句:我是 C++ 新手。

【问题讨论】:

  • 我不确定你在问什么。您是在问是否有办法在 C++ 中解析文件一次?答案当然是肯定的。也许您可以解释一下您尝试解决此问题的方法以及遇到的问题。
  • 首先,我调用jni方法parseFile();
  • 首先,我调用jni方法parseFile()(c++代码),返回一些数据给java。之后,根据用户的动作,比如触摸一个按钮,我会调用另一个jni方法selectNote(),在第二个jni方法的c++代码中,它会使用parseFile()产生的数据,所以我必须调用parseFile()再次在 selectNote() 中(c++ 代码)。
  • 如果你想缓存第一次运行parseFile()的结果,你可以先检查文件名是否相同且没有被修改,然后你可以重新使用数据上一次调用的结构(否则清理结构)

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


【解决方案1】:

我不确定我是否正确理解了您的问题。但我猜你想要做的是让某种 c++ 对象在多个 jni 调用中保持活跃。

你可以做很多事情。首先解析您的文件并将您的 c++ 对象存储在全局变量中。这是最简单的解决方案,但不是很好。

您还可以将 c++ 对象的生命周期移到 java 中。

jlong java_some_class_jni_method(...)
{
    .... parse your text file ....
    MyParseclass* cls = new MyParseclass(...);
    ....
    return (jlong) cls;
}

但请记住,您需要再次删除这个原生 c++ 类。所以你需要一个 jni 方法,并且一定要调用它。

void java_some_calls_jni_method(..., jlong clsPtr)
{
    MyParseclass* cls = (MyParseclass*)clsPtr;
    ... do maybe do something with cls and access the data...
    delete cls; // do not use the jlong again in any call
}

顺便说一句:如果您发布一些代码会更有帮助。但我希望这里的伪代码能有所帮助。

【讨论】:

  • 太棒了。这就是我要的。我会试试的。
  • @DavidGuo 删除 cls 后我仍然可以从 java long 访问对象,并且删除后本机内存不会减少
  • 我认为你必须在你的 java 代码中删除对 cls 的任何引用,这样就没有堆栈具有引用 cls 的变量,在这种情况下它将被删除。 (当创建框架从堆栈中退出时,JNI 本地引用被删除)。最后,原生对象将被垃圾回收,因为它们没有引用。
【解决方案2】:

这个问题和this one很像

这是我在 C++ 上保持对象活动以便从多个 JNI 调用中引用它们的解决方案:

Java

在 Java 方面,我正在创建一个带有 long 指针的类,以保留对 C++ 对象的引用。将 C++ 方法包装在 Java 类中,允许我们在多个活动中使用 C++ 方法。请注意,我在构造函数上创建了 C++ 对象,并在清理时删除了该对象。这对于防止内存泄漏非常重要:

public class JavaClass {
    // Pointer (using long to account for 64-bit OS)
    private long objPtr = 0;

    // Create C++ object
    public JavaClass() {
        createCppObject();
    }

    // Delete C++ object on cleanup
    public void cleanup() {
        deleteCppObject();
        this.objPtr = 0;
    }

    // Native methods
    public native void createCppObject();
    public native void workOnCppObject();
    public native void deleteCppObject();

    // Load C++ shared library
    static {
        System.loadLibrary("CppLib");
    }

}

C++

在 C++ 方面,我定义了创建、修改和删除对象的函数。值得一提的是,我们必须使用newdelete 将对象存储在HEAP 内存中,以使其在Java 类实例的整个生命周期中保持活动状态。我还将指向CppObject 的指针直接存储在JavaClass 中,使用getFieldIdSetLongFieldGetLongField

// Get pointer field straight from `JavaClass`
jfieldID getPtrFieldId(JNIEnv * env, jobject obj)
{
    static jfieldID ptrFieldId = 0;

    if (!ptrFieldId)
    {
        jclass c = env->GetObjectClass(obj);
        ptrFieldId = env->GetFieldID(c, "objPtr", "J");
        env->DeleteLocalRef(c);
    }

    return ptrFieldId;
}

// Methods to create, modify, and delete Cpp object
extern "C" {

    void Java_com_test_jnitest_JavaClass_createCppObject(JNIEnv *env, jobject obj) {
        env->SetLongField(obj, getPtrFieldId(env, obj), (jlong) new CppObject);
    }

    void Java_com_test_jnitest_JavaClass_workOnCppObject(JNIEnv *env, jobject obj) {
        CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj));

        // Write your code to work on CppObject here
    }

    void Java_com_test_jnitest_JavaClass_deleteCppObject(JNIEnv *env, jobject obj) {
        CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj));

        delete cppObj;
    } 

}

注意事项:

  • 与 Java 不同,C++ 没有垃圾回收,并且对象将驻留在 HEAP 内存中,直到您使用 delete
  • 我使用GetFieldIDSetLongFieldGetLongField 来存储来自C++ 的对象引用,但您也可以存储来自Java 的jlong 对象指针,如其他答案所述。
  • 在我的最终代码中,我将 JavaObject 类实现为 Parcelable,以便使用带有附加功能的 Intent 将我的类传递给多个活动。

【讨论】:

  • C++ 确实有垃圾收集。它还具有 Java 所没有的 RAII。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多