java 使用 JNI 从本机方法返回 null

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1166992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 15:28:57  来源:igfitidea点击:

Returning null from native methods using JNI

javac++java-native-interface

提问by

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.

我有一些本机代码返回一个 jbyteArray(Java 端的 byte[]),我想返回 null。但是,如果我简单地返回 0 代替 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.

更多信息:主要逻辑在Java中,使用native方法将一些数据编码成字节流。不要问..它必须这样做。最近,必须对本机代码进行一些更改,现在它运行得非常慢。经过一些实验,包括在返回之前注释掉本机方法中的所有代码,结果证明返回 0 会导致速度变慢。当返回一个实际的 jbyteArray 时,一切都很好。

Method signatures for my code:

我的代码的方法签名:

On the C++ side:

在 C++ 方面:

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

On the Java side:

在 Java 方面:

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.

有谁知道这可能发生的任何原因?从本机方法返回 NULL 代替 jbyteArray 是否有效,或者是否有另一个过程将 null 返回给 Java。不幸的是,我在谷歌上运气不佳。

Thanks!

谢谢!

EDIT: Added additional information.

编辑:添加了附加信息。

回答by Shlublu

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 jintArrayas 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.

我实际上只是尝试了一下,jintArray因为这是我的代码必须分配和返回的内容,除非发生错误(由与本主题无关的某些标准定义)在这种情况下它必须返回空结果。

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

碰巧返回NULL(定义为((void*)0))工作得很好,并且被解释为null返回到 Java 端。我没有注意到性能有任何下降。除非我错过了任何0没有void *演员表的回归,否则不会改变任何事情。

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

所以我认为这不是您遇到的放缓的原因。NULL看起来很好回来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 jintArrayof size 0, and a random jintArrayof 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.
  • 我确认,返回值与性能无关。我刚刚测试了一个相同的代码,一边返回一个空值,另一边返回一个对象 (a jintArray)。对于NULLjintArray大小为 0jintArray的 a和静态分配的几个 KB的随机数,性能是相似的。

  • 我还尝试更改调用者类字段的值,并以大致相同的性能返回 void。速度非常慢,可能是由于需要捕获该字段并设置它的反射代码。

  • 所有这些测试都是在 Android 下进行的,而不是在 Java 独立下进行​​的——也许这就是原因?(看评论):

    • 在 HAXM 下运行的 API 17 x86 模拟器
    • API 19 一个,在相同条件下运行
    • 两个 API 19 物理设备 - 一个华硕平板电脑和一个 Galaxy 5 - 在 Dalvik 下运行。

回答by xtofl

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 envobject 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.

您的代码中有一些不对称性让我印象深刻:您永远不会决定要返回的对象类型,除非返回“无”。显然env对象决定了如何分配 a javaSrray,那么为什么不让它返回某种空数组呢?在 jni 和 java 之间进行编组时,您返回的 0 可能需要以特殊方式处理。

回答by Christoffer

Have you tried returning a NULL reference?

您是否尝试过返回 NULL 引用?

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:

这是未经测试的(目前手头没有 JNI 开发环境),但您应该能够创建一个新的 NULL 全局引用并像这样返回它:

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.

编辑话虽如此,您检查是否发生异常,但不清除它。据我所知,这意味着它仍然在 Java 层中“抛出”,因此您应该能够将其用作错误指示符;那么函数返回什么并不重要。事实上,根据文档,在抛出异常时调用 ExceptionClear()/ExceptionDescribe() 以外的 JNI 函数并不“安全”。函数“慢”可能是由 ExceptionDescribe() 函数写入调试信息引起的。

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):

所以,如果我理解正确,这应该是一个行为良好的函数,在第一次发生错误时抛出异常,并在每次后续调用中返回 NULL(直到“错误”被清除):

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.

同样,这是未经测试的,因为我现在没有可用的 JNI 环境。