14

我有以下问题。我想编写一个使用我的遗留 C++ 类的 Android 应用程序。我必须在整个应用程序生命周期内保持 C++ 对象处于活动状态。

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

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

4

3 回答 3

19

是的,您可以执行与在 C# 中所做的完全相同的操作。

要创建新的 C++ 对象:

jlong
Java_package_name_new(JNIEnv *, jobject) {
  return (long)(new CPP_Object()); 
}

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

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

最后用类似的东西删除它:

void
Java_package_name_delete(JNIEnv *, jobject, jlong ptr) {
  delete (CPP_Object *)(ptr);
}

除了传递给所有需要它的方法之外,您还可以使用and方法ptr直接从 NDK 部分获取并设置它:这允许仅从代码的 NDK 部分管理 Java 变量,我觉得这更安全且更容易管理。SetLongFieldGetLongFieldptr

于 2013-11-06T09:56:36.397 回答
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");
    }

}

C++

在 C++ 方面,我正在定义创建、修改和删除对象的函数。值得一提的是,我们必须使用new并将delete对象存储在 HEAP 内存中,以使其在 Java 类实例的整个生命周期中保持活动状态。我还将指向CppObject直接的指针存储在JavaClassgetFieldIdSetLongField和中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;
    } 

}

笔记:

  • 与 Java 不同,C++ 没有垃圾收集,并且对象将存在于 HEAP 内存中,直到您使用delete.
  • 我正在使用GetFieldID,SetLongFieldGetLongField存储来自 C++ 的对象引用,但您也可以存储jlong来自 Java 的对象指针,如其他答案中所述。
  • 在我的最终代码中,我将JavaObject类实现为 a Parcelable,以便使用Intent附加功能将我的类传递给多个活动。
于 2017-06-20T18:07:04.273 回答
1

您还可以使用SWIG包装您的 C++ 代码,通过在 Java 中构造它来实例化您的本机对象,并且只要您持有对它的引用,它就不会被破坏(丢失引用使其有资格进行垃圾收集,这将当 Java 引用完成时调用 C++ Object 实例上的析构函数)。

但是,如果本机代码和 Java 代码之间的交互很少,那么使用 SWIG 可能太 OTT。

于 2013-11-06T10:17:38.683 回答