Android NDK, keeping live C++ objects

You could also wrap your C++ code using SWIG, where you instantiate your native Object by constructing it in Java, and as long as you hold a reference to it, it isn't destroyed (losing the reference makes it eligible for garbage collection which will invoke the destructor on the C++ Object instance when the Java reference is finalized).

However, if the interaction between native code and Java code is minimal, using SWIG might be too OTT.


Yes, you can do the exact same than what you did in C#.

To create your new C++ Object:

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

You can store the return value of this method in a Java ptr variable, and pass it to all NDK methods that need it:

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

And finally delete it with something like:

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

Instead of passing ptr to all methods that need it, you can also get it and set it directly from the NDK part using the SetLongField and GetLongField methods: this allows the Java ptr variable to be managed only from the NDK part of the code, which I find safer and easier to manage.


I'm a bit late to the conversation, but since I couldn't find an SO post that answers this question thoroughly, I'll post my solution.

Java

On the Java side, I am creating a class with a long pointer to keep a reference to the C++ object. Wrapping the C++ methods in a Java class, allows us to use the C++ methods in multiple activities. Notice that I am creating the C++ object on the constructor, and I am deleting the object on cleanup. This is very important in order to prevent memory leaks:

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++

On the C++ side, I am defining functions to create, modify and delete the object. It's important to mention that we have to use new and delete to store the object in the HEAP memory to keep it alive throughout the lifecycle of the Java class instances. I am also storing the pointer to CppObject straight in the JavaClass, using getFieldId, SetLongField, and 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;
    } 

}

NOTES:

  • Unlike Java, C++ does not have garbage collection, and the object will live on the HEAP memory, until you use delete.
  • I am using GetFieldID, SetLongField, and GetLongField to store the object reference from C++, but you could also store the jlong object pointer from Java as discussed on other answers.
  • On my final code, I implemented the JavaObject class as a Parcelable in order to pass my class to multiple activities using Intent with extras.