Android NDK,保存实时C++对象

Spo*_*ook 14 c++ java android android-ndk

我有一个以下问题.我想编写一个Android应用程序,它使用我的旧C++类.我必须在整个应用程序生命周期中保持C++对象的存活.

我在C#中编写了一个类似的应用程序,并通过将指向C++的指针传递给C#并使用IntPtr将其存储在那里来解决问题.然后,当我想在该对象上调用一个方法时,我只是将该指针再次传递给C++,转换为类指针并在其上调用一个方法.

如何在Java和Android NDK中实现类似的结果?Java是否支持存储指针?

mbr*_*non 17

是的,你可以做的与你在C#中做的完全相同.

要创建新的C++对象:

jlong
Java_package_name_new(JNIEnv *, jobject) {
  return (long)(new CPP_Object()); 
}
Run Code Online (Sandbox Code Playgroud)

您可以将此方法的返回值存储在Java ptr变量中,并将其传递给所有需要它的NDK方法:

void
Java_package_name_doSomething(JNIEnv *, jobject, jlong ptr) {
  CPP_Object *obj = (CPP_Object *)ptr;
  // do whatever you want with the object
}
Run Code Online (Sandbox Code Playgroud)

最后用以下内容删除它:

void
Java_package_name_delete(JNIEnv *, jobject, jlong ptr) {
  delete (CPP_Object *)(ptr);
}
Run Code Online (Sandbox Code Playgroud)

ptr您可以使用SetLongFieldGetLongField方法直接从NDK部分获取它并将其设置为所有方法,而不是传递给需要它的所有方法:这样只允许ptr从代码的NDK部分管理Java 变量,我发现它更安全,更容易管理.

  • 请使用`long` /`jlong​​`而不是`int` /`jint`作为指针; 否则,如果指针变为64位,代码将会中断. (3认同)
  • 我发现使用 GetLongField 的缺点是您必须将字段 ID 存储为静态变量,如果您需要拥有 C 对象的多个实例,这可能会成为问题。或者,您可以在每次调用 JNI 时调用“GetFieldID”,但这会产生查找成本。或者您可以将字段 ID 传递回 Java,这有点混乱,因为现在您必须在 Java 类中保留字段和字段 ID 的成员。不过,我不是 Java 专家,欢迎任何人告诉我我错了,这样我就可以学到一些东西。 (2认同)

Jai*_*tes 5

我的谈话有点晚了,但由于我找不到彻底回答这个问题的 SO 帖子,我将发布我的解决方案。

爪哇

在 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");
    }

}
Run Code Online (Sandbox Code Playgroud)

C++

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

// 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;
    } 

}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 与 Java 不同,C++ 没有垃圾收集,并且对象将存在于 HEAP 内存中,直到您使用delete.
  • 我正在使用GetFieldID,SetLongFieldGetLongField来存储来自 C++ 的对象引用,但您也可以jlong像其他答案中讨论的那样存储来自 Java的对象指针。
  • 在我的最终代码中,我将JavaObject类实现为 aParcelable以便将我的类传递给使用Intent附加功能的多个活动。