Возврат null из собственных методов с использованием JNI

У меня есть собственный код, который возвращает jbyteArray (так что byte [] на стороне Java), и я хочу вернуть null. Тем не менее, я сталкиваюсь с проблемами, если я просто возвращаю 0 вместо jbyteArray.

Дополнительная информация: Основная логика в Java, собственный метод используется для кодирования некоторых данных в поток байтов. Не спрашивайте, как это сделать. Недавно нативный код пришлось немного изменить, и теперь он работает ужасно ужасно медленно. После некоторых экспериментов, которые включали комментирование всего кода в нативном методе перед возвратом, оказывается, что возвращение 0 вызывает замедление. При возврате реального jbyteArray все в порядке.

Подписи метода для моего кода:

На стороне С++:

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

На стороне Java:

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

Нативный код выглядит примерно так:

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

Кто-нибудь знает какие-либо причины, по которым это может случиться? Правильно ли возвращать NULL из собственного метода вместо jbyteArray или есть другая процедура, возвращающая null обратно на Java. К сожалению, мне не повезло с Google.

Спасибо!

EDIT: добавлена ​​дополнительная информация.

Ответ 1

Мне не нравится решение вернуть NULL. Если возможно, вы должны вернуть несколько пустых реализаций, например

return new ArrayList();
return new byte[0];
return new EmptySomeObjectImpl();

Чем вы не беспокоитесь о NullPointerExceptions

if (error == ERROR) {
    return env->NewByteArray(0);
}

ИЗМЕНИТЬ

Попробуйте вернуть это всегда, если вы не хотите реализовать EmptyObjects.

if (error == ERROR) {
     return javaSrray; // Please test it and than return javaSrray always without if else
}
else {
    return javaSrray; // Works perfectly.
}

Ответ 2

Это старый вопрос, но у меня было это еще минуту назад...

Вы говорите в своем вопросе:

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

Я просто попробовал на самом деле с jintArray, так как это мой код должен выделять и возвращать, если ошибка не возникает (определяется некоторыми критериями, не относящимися к этой теме), и в этом случае она должна вернуть нулевой результат.

Бывает, что возвращение NULL (определенное как ((void*)0)) отлично работает и интерпретируется как NULL, когда возвращается на сторону Java. Я не заметил никакой деградации выступлений. И если я не пропустил ничего, возвращающ 0 без void *, приведение ничего не изменило бы к этому.

Поэтому я не думаю, что это было причиной замедления, с которым вы столкнулись. NULL выглядит просто отлично, чтобы вернуть NULL.

ИЗМЕНИТЬ:

  • Я подтверждаю, что возвращаемое значение не имеет ничего общего с показателями. Я просто проверил один и тот же код, возвращающий нулевое значение на стороне, а его копия возвращает объект (a jintArray) с другой. Исполнения аналогичны для NULL, a jintArray размера 0, а случайный jintArray из нескольких КБ, выделенных статически.

  • Я также попытался изменить значение поля класса вызывающего абонента и сохранить пустоту с примерно одинаковыми характеристиками. Очень немного медленнее, вероятно, из-за кода отражения, необходимого для того, чтобы поймать это поле и установить его.

  • Все эти тесты были сделаны под Android, а не под Java standalones - может быть, поэтому? (см. комментарии):

    • Эмулятор API 17 x86, работающий под управлением HAXM
    • API 19, работающий в тех же условиях
    • Два физических устройства API 19 - планшет Asus и Galaxy 5 - работают под Dalvik.

Ответ 3

Там какая-то асимметрия в вашем коде, которая поразила мой взгляд: вы никогда не решаете вернуть тип объекта, кроме как при возвращении "ничего". По-видимому, объект env решает, как выделить javaSrray, так почему бы не попросить его вернуть какой-то пустой массив? Возможно, что возвращаемое вами 0 должно обрабатываться особым образом при маршалинге между jni и java.

Ответ 4

Вы пытались вернуть ссылку NULL?

Это непроверено (на данный момент не существует среды разработки JNI), но вы должны иметь возможность создать новую глобальную ссылку на NULL и вернуть ее следующим образом:

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

EDIT. При этом вы проверяете, произошло ли исключение, но не очистите его. Это, насколько я понимаю, означает, что он все еще "брошен" на уровне Java, поэтому вы должны использовать его как индикатор ошибки; то не имеет значения, что возвращает функция. На самом деле вызов функции JNI, отличной от ExceptionClear()/ExceptionDescribe(), когда генерируется исключение, не является "безопасным" в соответствии с документацией. То, что функции "медленные" могут быть вызваны функцией ExceptionDescribe(), пишущей отладочную информацию.

Итак, если я правильно это понимаю, это должна быть хорошо продуманная функция, генерирующая исключение при первом возникновении ошибки и возвращающая 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);
}

Опять же, это непроверено, так как я не имею доступную среду JNI.