Search code examples
javac++java-native-interface

Store a c++ object instance inside JNI jobject and retrieve later


I have a JNI class with methods init() work(), and cleanup(). On the C++ side I create an instance of a C++ class Foo during init(), then call some methods on it during work(), and finally delete it inside cleanup(). Right now I store instance of Foo as a global singleton on the C++ so that I can retrieve it from the different JNI calls. What I would really like to do is store a pointer to the Foo instance inside the jobject instance that gets passed to each JNI call, so that I can avoid having a global singleton and also so that I can support multiple instances of Foo. Is something like this possible?


Solution

  • You can store a pointer to your C++ object as a Java class member. For example, in Java:

    class Foo
    {
        public long ptr = 0;
    
        public native void init();
        public native void work();
        public native void cleanup();
    }
    

    And in C++:

    jfieldID getPtrFieldId(JNIEnv * env, jobject obj)
    {
        static jfieldID ptrFieldId = 0;
    
        if (!ptrFieldId)
        {
            jclass c = env->GetObjectClass(obj);
            ptrFieldId = env->GetFieldID(c, "ptr", "J");
            env->DeleteLocalRef(c);
        }
    
        return ptrFieldId;
    }
    
    class Foo
    {
        /* ... */
    };
    
    extern "C"
    {
        void Java_Foo_init(JNIEnv * env, jobject obj)
        {
            env->SetLongField(obj, getPtrFieldId(env, obj), (jlong) new Foo);
        }
    
        void Java_Foo_work(JNIEnv * env, jobject obj)
        {
            Foo * foo = (Foo *) env->GetLongField(obj, getPtrFieldId(env, obj));
    
            foo->work();
        }
    
        void Java_Foo_cleanup(JNIEnv * env, jobject obj)
        {
            Foo * foo = (Foo *) env->GetLongField(obj, getPtrFieldId(env, obj));
    
            delete foo;
        }
    }