Keep Objects in JNI for speeding up

In Android, sometimes we need to use JNI to process data. Not only we can use old function calls in C/C++, but we can speed up data handling process. Cause C/C++ performance is still better than Java code.

For JNI, a better way to handle data is to keep that as global variables. It will have better performance instead of keeping creating new one.

For example, your java code might call following jni function via “native” keyword:

jobjectArray array; 
void *arrPtr; 

JNIEXPORT jobject JNICALL Java_Call_JNI_SetArray
  (JNIEnv *env, jobject thiz, jint tag, jobjectArray arr) {

    // store into global static storage
    array = arr;

    // keep a pointer to this array for manipulating
    ptr = env->GetPrimitiveArrayCritical(arr, 0);
}

After calling this jni function and saving arr data, you might need to process array data:

JNIEXPORT jstring JNICALL Java_Call_JNI_ProcessData
  (JNIEnv *env, jclass thiz, int index) {

   ... 

   // get element from array, and do something. 
   jobject object = env->GetObjectArrayElement(array, index);  

   ...
}

Then, you will find Debug Console keeps firing!!!!!

JNI DETECTED ERROR IN APPLICATION: jarray is an invalid local reference: 0x5 (0xdead4321)
02-06 22:04:32.569 12413-17393/your.app.package.name A/art: art/runtime/check_jni.cc:81]     in call to GetObjectArrayElement
02-06 22:04:32.569 12413-17393/your.app.package.name A/art: art/runtime/check_jni.cc:81]     from void your.app.package.name.function.call(java.nio.ByteBuffer, byte[], int)

That’s so annoying, why it shows these error log and crashed? I’ve done everything!! You might keep asking yourself this question.
But, you think you did well, actually, you didn’t. You need to set a global reference for your Array variable. Let’s do it again.

jobjectArray array; 
void *arrPtr; 

JNIEXPORT jobject JNICALL Java_Call_JNI_SetArray
  (JNIEnv *env, jobject thiz, jint tag, jobjectArray arr) {

    // store into global static storage, and set as a global reference
    array = (jobjectArray) env->NewGlobalRef(arr);

    // keep a pointer to this array for manipulating
    ptr = env->GetPrimitiveArrayCritical(arr, 0);
}

And, remember to delete that global reference when you leave your app.

JNIEXPORT jboolean JNICALL Java_Call_JNI_ReleaseObject
  (JNIEnv *env, jobject thiz, jint tag, jobject handle) {

    env->ReleasePrimitiveArrayCritical(array, ptr, 0);
    env->DeleteGlobalRef(array);
    
    return true;
}

And, finally, a small tip, using GetPrimitiveArrayCritical is good for performance. But it has some restrictions. Please check JNI document;

Happy Coding !!

Reference:
[JNI Document]
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetPrimitiveArrayCritical_ReleasePrimitiveArrayCritical

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s