Возврат 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.