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

时间:2013-11-06 09:44:33

标签: java android c++ android-ndk

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

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

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

3 个答案:

答案 0 :(得分:17)

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

创建新的C ++对象:

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

您可以将此方法的返回值存储在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
}

最后用以下内容删除它:

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

不是将ptr传递给需要它的所有方法,而是使用SetLongFieldGetLongField方法获取它并直接从NDK部分设置它:这允许Java ptr变量只能从代码的NDK部分进行管理,我觉得它更安全,更易于管理。

答案 1 :(得分:2)

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

<强>爪哇

在Java方面,我正在创建一个带有long指针的类,以保持对C ++对象的引用。在Java类中包装C ++方法允许我们在多个活动中使用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类实例的整个生命周期中保持活动状态。我还使用CppObjectJavaClassgetFieldIdSetLongField的指针直接存储在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
  • 我正在使用GetFieldIDSetLongFieldGetLongField来存储来自C ++的对象引用,但您也可以存储来自Java的jlong对象指针,如上所述其他答案。
  • 在我的最终代码中,我将JavaObject类作为Parcelable实现,以便使用Intent和其他内容将我的课程传递给多个活动。

答案 2 :(得分:1)

你也可以使用SWIG包装你的C ++代码,你可以通过在Java中构建它来实例化你的本机Object,只要你持有它的引用,它就不会被破坏(丢失引用使得它有资格进行垃圾收集,它将在Java参考完成时调用C ++ Object实例上的析构函数。)

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