JNA, здається, трохи простіше використовувати для виклику нативного коду порівняно з JNI. У яких випадках ви б використовували JNI над JNA?
JNA, здається, трохи простіше використовувати для виклику нативного коду порівняно з JNI. У яких випадках ви б використовували JNI над JNA?
Відповіді:
Це проблеми, з якими я стикався. Можливо, є і більше. Але загалом продуктивність не відрізняється від jna та jni, тому де б ви не могли використовувати JNA, використовуйте її.
EDIT
Ця відповідь здається досить популярною. Ось ось кілька доповнень:
Отже, я все ще вважаю, що там, де це можливо, краще використовувати JNA або BridJ, і повернутись до jni, якщо продуктивність є критичною, тому що якщо вам потрібно часто дзвонити на рідні функції, помітний удар.
JNIEXPORT
функції. Зараз я досліджую JavaCpp як варіант, який використовує JNI, але я не думаю, що ванільний JNI це підтримує. Чи є спосіб викликати функції C ++, використовуючи ванільний JNI, який я переглядаю?
На таке загальне запитання важко відповісти. Я вважаю, що найбільш очевидна відмінність полягає в тому, що при JNI перетворення типів здійснюється на рідній стороні Java / рідної межі, тоді як при JNA перетворення типів здійснюється в Java. Якщо ви вже відчуваєте себе досить комфортно з програмуванням на C і вам доведеться самостійно реалізувати якийсь нативний код, я вважаю, що JNI не здасться занадто складним. Якщо ви Java-програміст і вам потрібно посилатися лише на сторонню рідну бібліотеку, використання JNA - це, мабуть, найпростіший шлях, щоб уникнути можливо не настільки очевидних проблем з JNI.
Хоча я ніколи не визначав жодних відмінностей, я б, зважаючи на дизайн, принаймні припускав, що перетворення типу з JNA в деяких ситуаціях буде гірше, ніж з JNI. Наприклад, передаючи масиви, JNA буде перетворювати їх з Java в початкові на початку кожного виклику функції і назад в кінці виклику функції. За допомогою JNI ви можете керувати собою, коли генерується нативний "перегляд" масиву, потенційно створюючи лише перегляд частини масиву, зберігати подання через кілька викликів функцій, а в кінці звільняти подання та вирішувати, чи хочете ви щоб зберегти зміни (потенційно вимагати копіювання даних назад) або відхилити зміни (копія не потрібна). Я знаю, що ви можете використовувати власний масив для функціональних викликів з JNA за допомогою класу Memory, але для цього також буде потрібно копіювання пам'яті, що може бути непотрібним для JNI. Різниця може не бути актуальною, але якщо ваша первісна мета - збільшити продуктивність додатків, реалізуючи його частини в кодовому коді, використання гірших технологій мосту здається не найбільш очевидним вибором.
Це тільки те, що я можу придумати вгорі голови, хоча я і не є важким користувачем. Також здається, що ти можеш уникнути JNA, якби ти хотів кращого інтерфейсу, ніж той, який вони надають, але ти можеш кодувати навколо цього в Java.
До речі, в одному з наших проектів ми зберегли дуже маленький друк JNI. Ми використовували буфери протоколів для представлення наших об’єктів домену і, таким чином, мали лише одну нативну функцію, щоб з'єднати Java та C (тоді, звичайно, ця функція C викликала б купу інших функцій).
Це не пряма відповідь, і я не маю досвіду роботи з JNA, але, коли я дивлюся на проекти, що використовують JNA, і бачу такі назви, як SVNKit, IntelliJ IDEA, NetBeans IDE тощо, я схильний вважати, що це досить пристойна бібліотека.
Насправді я, безумовно, думаю, що використовував би JNA замість JNI, коли мені довелося, як це справді виглядає простіше, ніж JNI (який має нудний процес розвитку). Шкода, що JNA наразі не була звільнена.
Якщо ви хочете, щоб продуктивність JNI була вражена її складністю, ви можете скористатися інструментами, які генерують прив'язки JNI автоматично. Наприклад, JANET (відмова від відповідальності: я написав це) дозволяє змішувати Java та код C ++ в одному вихідному файлі, наприклад, здійснювати дзвінки з C ++ на Java, використовуючи стандартний синтаксис Java. Наприклад, ось як ви надрукували рядок C на стандартний вихід Java:
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
Потім JANET переводить Java-вбудовану Java у відповідні дзвінки JNI.
Я фактично робив кілька простих орієнтирів з JNI та JNA.
Як вже вказували інші, JNA - це для зручності. Вам не потрібно збирати або писати нативний код під час використання JNA. Навантажувач рідної бібліотеки JNA також є одним з найкращих / найпростіших у користуванні, які я коли-небудь бачив. На жаль, ви не можете використовувати його для JNI, здається. (Тому я написав альтернативу для System.loadLibrary (), яка використовує конвенцію контуру JNA та підтримує безперешкодне завантаження з classpath (тобто, банки).)
Однак продуктивність JNA може бути набагато гіршою, ніж у JNI. Я зробив дуже простий тест, який назвав просту функцію приросту цілого числа "повернути arg + 1;". Оцінки, зроблені за допомогою jmh, показали, що виклики JNI на цю функцію в 15 разів швидше, ніж JNA.
Більш "складний" приклад, коли нативна функція підсумовує цілий масив із 4 значень, все ще показала, що продуктивність JNI в 3 рази швидша, ніж JNA. Зменшена перевага, мабуть, пояснюється тим, як ви отримуєте доступ до масивів в JNI: мій приклад створив деякі речі та випустив їх знову під час кожної операції підбиття підсумків.
Код та результати тестування можна знайти на сайті github .
Я досліджував JNI та JNA для порівняння продуктивності, тому що нам потрібно було вирішити одну з них, щоб викликати dll в проекті, і ми мали обмеження в реальному часі. Результати показали, що JNI має більшу ефективність, ніж JNA (приблизно в 40 разів). Можливо, є хитрість для кращої роботи в JNA, але це дуже повільно для простого прикладу.
Якщо я чогось не пропускаю, чи не головна відмінність JNA від JNI в тому, що з JNA ви не можете зателефонувати на код Java з нативного (C) коду?
У моєму конкретному застосуванні JNI виявився набагато простішим у використанні. Мені потрібно було читати і записувати безперервні потоки до і з послідовного порту - і більше нічого. Замість того, щоб спробувати вивчити дуже задіяну інфраструктуру в JNA, мені було набагато простіше прототипувати власний інтерфейс у Windows із спеціальною DLL-програмою, яка експортувала лише шість функцій: