Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

keep some sort of c++ object alive over multiple jni calls

My java code will call the exist c++ code to parse the file. it will generate an object which keep many data. I will call the jni's second method to access such data, When I call the second method, I have to parse the file again. it is obviously the right behavior.

Is there a way to handle this ? BTW: I am newly to c++.

like image 850
David Guo Avatar asked Dec 06 '11 09:12

David Guo


People also ask

What is Jclass in JNI?

typedef jobject jclass; In C++, JNI introduces a set of dummy classes to enforce the subtyping relationship. For example: class _jobject {}; class _jclass : public _jobject {}; ...

What is JNIEnv * env?

jobject NewGlobalRef(JNIEnv *env, jobject obj); Creates a new global reference to the object referred to by the obj argument. The obj argument may be a global or local reference.

What is a JNI wrapper?

JNI is the Java Native Interface. It defines a way for the bytecode that Android compiles from managed code (written in the Java or Kotlin programming languages) to interact with native code (written in C/C++).

What is JNI Jobject?

jobject thiz means the this in java class. Sometimes if you create a static native method like this. void Java_MyClass_method1 (JNIEnv *, jclass); jclass means the class itself. Follow this answer to receive notifications.


2 Answers

I am not sure if I understand your question correct. But I guess what you want to do is to keep some sort of c++ object alive over multiple jni calls.

You can do multiple things. First parse your file and store your c++ object in a global variable. This is the simplest solution but not a nice one.

You can also move the life cycle of your c++ object into java.

jlong java_some_class_jni_method(...)
{
    .... parse your text file ....
    MyParseclass* cls = new MyParseclass(...);
    ....
    return (jlong) cls;
}

But keep in mind that you need to delete this native c++ class again. So you need a jni method to this and be sure to call it.

void java_some_calls_jni_method(..., jlong clsPtr)
{
    MyParseclass* cls = (MyParseclass*)clsPtr;
    ... do maybe do something with cls and access the data...
    delete cls; // do not use the jlong again in any call
}

BTW: It would be much more helpful if you would post some code. But I hope this pseudo code here helps a little.

like image 182
mkaes Avatar answered Nov 10 '22 15:11

mkaes


This question is very similar to this one

Here's my solution to keeping objects alive on C++ in order to reference them from multiple JNI calls:

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.
like image 25
Jaime Ivan Cervantes Avatar answered Nov 10 '22 16:11

Jaime Ivan Cervantes