I've got a following problem. I want to write an Android application, which uses my legacy C++ classes. I have to keep a C++ object alive during the whole application lifetime.
I wrote a similar application in C# and solved the problem by passing a pointer to a C++ class to C# and storing it there using IntPtr. Then, when I wanted to call a method on that object, I simply passed that pointer again to C++, converted to a class pointer and called a method on it.
How can I achieve similar result in Java and Android NDK? Does Java support storing pointers?
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:
delete
.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.JavaObject
class as a Parcelable
in order to pass my class to multiple activities using Intent
with extras.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With