Android JNI c++ pointers to Java.

TwitterGoogle+FacebookShare

When working with Native on Android, it is many times needed to have a Native representation of a Java Object. This native Object should mainly behave like a proxy to the Java object which is mainly straightforward. Since the native support android is extremely limited, the use cases for this are almost infinite. From proper concurrent HttpRequest handling, to send a push notification, etc.
The tricky part is when the Java object should notify back the Native object about an event with origin on the Java side. For such scenario, it is a must to keep the Native reference object in Java, so that Java can notify back the expected Native object that initiated the call. To do so, the pointer to this must be sent from native to Java. A pointer, is just a memory address, and a casting to long will do. With the subtle detail that this will mangle the pointer and your code will break.


// somewhere in your c/c++ file:
// 
// build a Java object to keep as native-to-java proxy.
// We instantiate from a Java constructor with a long parameter.
// The Java long is the c++ pointer object.
JNIEnv*   env=        ...;
jclass    clazz=      env->FindClass( className );
jmethodID constructor=env->GetMethodID( clazz, "", constructorSignature );

/////////// WRONG . SEG_MAPPER courtesy of mangled pointer 
long      thisPtr=    (long)this;
jobject   obj=        env->NewObject( clazz, constructor, thisPtr );

This code will fail. Converting ‘this’ to long won’t work. Instead, use the following:


////////// RIGHT. This will work. The call to 
// jlong(this) will handle endianness and type conversion.
jlong   thisPtr= jlong(this);  
jobject obj=     env->NewObject( clazz, constructor, thisPtr );

Now, there’s the part where we want to notify back from Java to native. Java must have a ‘native’ qualified method, like for example:


// somewhere in your .java file

public class Clazz {
   
   ...
 
   /**
    * ...
    * @param nativePtr the native object pointer got at construction time.
    */
   private static native void onProgress( long nativePtr, float value );
}

And the implementation of this native function could be:


// somewhere in your c/c++ file

void JNICALL Java_fully_qualified_package_CLASSNAME_METHODNAME(
    JNIEnv* env, jobject thiz, jlong nativePtr, jfloat progress) {

    // nativePtr is safe to reinterpret as the original 
    // 'this' promoted to 'jlong' object's memory address.
    reinterpret_cast(nativePtr)->...

}

As a warning note, watch out when calling jlong(this). If the object is temporary, whenever Java calls back to Native you’ll have a guaranteed application crash. It is important to have Object lifecycle so that you can free the native object pointers when it will safe to do so.