user21037
user21037

Reputation:

Returning null from native methods using JNI

I have some native code which returns a jbyteArray (so byte[] on the Java side) and I want to return null. However, I run into problems if I simply return 0 in place of the jbyteArray.

Some more information: The main logic is in Java, the native method is used to encode some data into a byte stream. don;t ask.. it has to be done like this. Recently, the native code had to be changed a bit and now it runs horribly horrible slow. After some experimentation, which included commenting out all code in the native method before the return, it turns out that returning 0 causes the slowdown. When returning an actual jbyteArray, everything is fine.

Method signatures for my code:

On the C++ side:

extern "C" JNIEXPORT jbyteArray JNICALL Java_com_xxx_recode (JNIEnv* env, jclass java_this, jbyteArray origBytes, jobject message)

On the Java side:

private static native byte[] recode(byte[] origBytes, Message message);

The native code looks something like this:

jbyteArray javaArray;
if (error != ERROR) {
    // convert to jbyteArray
    javaArray = env->NewByteArray((jsize) message.size);
    env->SetByteArrayRegion(java_array, 0, message.size, reinterpret_cast<jbyte*>(message.buffer()));
    if (env->ExceptionOccurred()) {
        env->ExceptionDescribe();
        error = ERROR;
    }
}
if (error == ERROR) {
    return 0; // Does NOT work - doesn't crash, just slows everything down horrible.
}
else {
    return javaArray; // Works perfectly.
}

Does anyone know of any reasons that this could happen? Is it valid to return NULL from a native method in place of a jbyteArray, or is there another procedure to return null back to Java. Unfortunately, I had no luck on Google.

Thanks!

EDIT: Added additional information.

Upvotes: 17

Views: 19127

Answers (3)

Shlublu
Shlublu

Reputation: 11027

This is an old question but I had it too a minute ago...

You say in your question:

return 0; // Does NOT work - doesn't crash, just slows everything down horrible.

I just gave a try actually, with a jintArray as this is what my code has to allocate and return, unless an error happens (defined by some criteria not related to this topic) in which case it has to return a null result.

It happens that returning NULL (defined as ((void*)0)) works perfectly and is interpreted as null when back to the Java side. I didn't notice any degradation of the performances. And unless I missed anything returning 0 with no void * cast would not change anything to this.

So I don't think this was the cause of the slowdown you encountered. NULL looks just fine to return null.

EDIT:

  • I do confirm, the return value has nothing to do with performances. I just tested a same code returning a null value on a side, and its counterpart returning an object (a jintArray) on the other. Performances are similar for NULL, a jintArray of size 0, and a random jintArray of a few KBs allocated statically.

  • I also tried changing the value of a caller class's field, and returing void, with roughly the same performances. A very very little bit slower, probably due to the reflection code needed to catch that field and set it.

  • All these tests were made under Android, not under Java standalones - maybe this is why? (see comments):

    • An API 17 x86 emulator running under HAXM
    • An API 19 one, running under the same conditions
    • Two API 19 physical devices - an Asus tablet and a Galaxy 5 - running under Dalvik.

Upvotes: 8

Christoffer
Christoffer

Reputation: 12910

Have you tried returning a NULL reference?

This is untested (don't have a JNI development environment at hand at the moment) but you should be able to create a new global reference to NULL and return it like this:

return (*env)->NewGlobalRef(env, NULL);

EDIT That being said, you check if an exception occurs, but do not clear it. That, as far as I can understand, means that it is still "thrown" in the Java layer, so you should be able to use just that as an error indicator; then it does not matter what the function returns. In fact, calling a JNI function other than ExceptionClear()/ExceptionDescribe() when an exception is thrown is not "safe" according to the documentation. That the functions is "slow" might be caused by the ExceptionDescribe() function writing debugging information.

So, if I understand this correctly, this should be a well-behaved function throwing an exception the first time an error occurs, and returning NULL on each subsequent call (until 'error' is cleared):

if (error != ERROR) {
    jbyteArray javaArray = env->NewByteArray((jsize) message.size);
    env->SetByteArrayRegion(javaArray, 0, message.size, reinterpret_cast<jbyte*>(message.buffer()));
    if (env->ExceptionOccurred()) {
        error = ERROR;
        return 0;
    }
    return javaArray;
} else {
    return env->NewGlobalRef(NULL);
}

Again, this is untested since I dont have a JNI environment available right now.

Upvotes: -2

xtofl
xtofl

Reputation: 41519

There's some asymmetry in your code that struck my eye: you never decide upon the type of object to return, except when returning 'nothing'. Apparently the env object decides how to allocate a javaSrray, so why not ask it to return some kind of empty array? It may be possible that the 0 that you return needs to be handled in a special way while marshaling between jni and java.

Upvotes: 0

Related Questions