Java – Android java + native data visibility

Android java + native data visibility… here is a solution to the problem.

Android java + native data visibility

Let’s say I have two threads (except for the main thread) in my game application:

  • GLRenderer thread (provided by GLSurfaceView.Renderer for Android).
  • Another thread (game thread).

Both threads use JNI to call some of the application’s C++ (that is, Android NDK) components.

Let’s say I allocate a direct IntBuffer in Java (e.g. from a GLRenderer thread, but don’t assume so). Fact:

  • This direct buffer is read by native code from the GLRenderer thread (that is, by the C++ component called via JNI).
  • This direct buffer is sometimes written from other threads (game threads).

What is the (best) way to synchronize (effectively ensure data visibility) in the following two cases, i.e. to ensure that the native code in GLRenderer code sees the latest IntBuffer content?

  • Scenario #1: The game thread’s Java code writes to IntBuffer (e.g. via IntBuffer.put()).
  • Scenario #2: Native code called from the game thread writes to IntBuffer

I think the standard Java sync works for both cases :

public void onDrawFrame(GL10 gl) { // the GLRenderer thread
    // ...
    synchronized (obj) {
        callNativeCode1();  a JNI call; this is where the C++ native code reads the IntBuffer
    }

}

public void run() { // the game thread
    // ...

synchronized (obj) {
        intBuffer.put(...);  writing the buffer from managed code
    }

// ...
    synchronized (obj) {
        callNativeCode2();  a JNI call; writing the buffer from C++ native code
    }
}

Solution

Not knowing the details of sharing memory with JNI, but I recommend using AtomicIntegerArray.

Your choice is:

  1. synchronized – you need to somehow implement the same functionality in JNI – not easy.
  2. volatile – making the entire array volatile will be a problem.
  3. atomics – I would say the best route.

See also Package java.util.concurrent.atomic for:

The AtomicIntegerArray, AtomicLongArray, and AtomicReferenceArray classes further extend atomic operation support to arrays of these types. These classes are also notable in providing volatile access semantics for their array elements, which is not supported for ordinary arrays.

This basically ensures that as long as the JNI code doesn’t do anything to bypass Java’s cache flush semantics, then the JNI package should see a consistent and updated data view.

I

would recommend some important research to confirm this, but I believe this is the only way for you to achieve your goals.

Related Problems and Solutions