JNA wydaje się nieco łatwiejsze w użyciu do wywoływania kodu natywnego w porównaniu z JNI. W jakich przypadkach użyłbyś JNI zamiast JNA?
JNA wydaje się nieco łatwiejsze w użyciu do wywoływania kodu natywnego w porównaniu z JNI. W jakich przypadkach użyłbyś JNI zamiast JNA?
Odpowiedzi:
Oto problemy, z którymi się spotkałem. Może jest więcej. Ale ogólnie wydajność nie różni się zbytnio między jna i jni, więc gdziekolwiek możesz użyć JNA, używaj go.
EDYTOWAĆ
Ta odpowiedź wydaje się być dość popularna. Oto kilka dodatków:
Tak więc nadal uważam, że wszędzie tam, gdzie to możliwe, lepiej jest używać JNA lub BridJ i powrócić do jni, jeśli wydajność jest krytyczna, ponieważ jeśli musisz często wywoływać funkcje natywne, zauważalny jest spadek wydajności.
JNIEXPORT
funkcje globalne . Obecnie badam JavaCpp jako opcję, która używa JNI, ale nie sądzę, aby wanilia JNI to obsługiwała. Czy istnieje sposób wywoływania funkcji składowych C ++ przy użyciu waniliowego JNI, którego przeoczam?
Trudno odpowiedzieć na tak ogólne pytanie. Przypuszczam, że najbardziej oczywistą różnicą jest to, że w przypadku JNI konwersja typów jest implementowana po natywnej stronie granicy Java / native, podczas gdy w przypadku JNA konwersja typów jest zaimplementowana w Javie. Jeśli już czujesz się dobrze z programowaniem w C i musisz samodzielnie zaimplementować kod natywny, założyłbym, że JNI nie wydaje się zbyt skomplikowane. Jeśli jesteś programistą Java i potrzebujesz tylko wywołać natywną bibliotekę innej firmy, użycie JNA jest prawdopodobnie najłatwiejszą drogą do uniknięcia być może nie tak oczywistych problemów z JNI.
Chociaż nigdy nie testowałem żadnych różnic, ze względu na projekt, przynajmniej przypuszczam, że konwersja typów z JNA w niektórych sytuacjach będzie działać gorzej niż z JNI. Na przykład podczas przekazywania tablic JNA konwertuje je z języka Java na natywne na początku każdego wywołania funkcji iz powrotem na końcu wywołania funkcji. Dzięki JNI możesz kontrolować siebie, kiedy generowany jest natywny „widok” tablicy, potencjalnie tworząc tylko widok części tablicy, zachować widok na kilka wywołań funkcji, a na koniec zwolnić widok i zdecydować, czy chcesz aby zachować zmiany (potencjalnie wymagające skopiowania danych z powrotem) lub odrzucić zmiany (kopiowanie nie jest wymagane). Wiem, że możesz używać natywnej tablicy w wywołaniach funkcji z JNA przy użyciu klasy Memory, ale będzie to również wymagało kopiowania pamięci, co może być niepotrzebne w przypadku JNI. Różnica może nie mieć znaczenia, ale jeśli Twoim pierwotnym celem jest zwiększenie wydajności aplikacji poprzez implementację jej części w kodzie natywnym, użycie gorszej technologii mostu nie wydaje się być najbardziej oczywistym wyborem.
To tylko to, co mogę wymyślić z czubka głowy, chociaż nie jestem zbyt ciężkim użytkownikiem. Wydaje się również, że możesz uniknąć JNA, jeśli chcesz mieć lepszy interfejs niż ten, który zapewniają, ale możesz go kodować w Javie.
Nawiasem mówiąc, w jednym z naszych projektów zachowaliśmy bardzo mały ślad stopy JNI. Użyliśmy buforów protokołów do reprezentowania naszych obiektów domeny i dlatego mieliśmy tylko jedną natywną funkcję łączącą Javę i C (wtedy oczywiście ta funkcja C wywoływałaby kilka innych funkcji).
Nie jest to bezpośrednia odpowiedź i nie mam doświadczenia z JNA, ale kiedy patrzę na projekty wykorzystujące JNA i widzę nazwy takie jak SVNKit, IntelliJ IDEA, NetBeans IDE, itp., Wierzę, że jest to całkiem przyzwoita biblioteka.
Właściwie, zdecydowanie myślę, że użyłbym JNA zamiast JNI, kiedy musiałem, ponieważ wygląda to na prostsze niż JNI (który ma nudny proces programowania). Szkoda, JNA nie została wydana w tym czasie.
Jeśli chcesz wydajności JNI, ale zniechęca Cię jego złożoność, możesz rozważyć użycie narzędzi, które automatycznie generują powiązania JNI. Na przykład JANET (zastrzeżenie: napisałem to) pozwala na mieszanie kodu Java i C ++ w jednym pliku źródłowym, a także np. Wykonywanie wywołań z C ++ do Javy przy użyciu standardowej składni Javy. Na przykład, oto jak wypisać ciąg C na standardowe wyjście Java:
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
Następnie JANET tłumaczy język Java z wbudowanym znacznikiem wstecznym na odpowiednie wywołania JNI.
Właściwie zrobiłem kilka prostych testów porównawczych z JNI i JNA.
Jak inni już zauważyli, JNA jest dla wygody. Nie musisz kompilować ani pisać kodu natywnego, gdy używasz JNA. Natywny program ładujący biblioteki JNA jest również jednym z najlepszych / najłatwiejszych w użyciu, jakie kiedykolwiek widziałem. Niestety, wydaje się, że nie możesz go używać do JNI. (Dlatego napisałem alternatywę dla System.loadLibrary (), która korzysta z konwencji ścieżki JNA i obsługuje bezproblemowe ładowanie ze ścieżki klas (tj. Słoików).)
Jednak wydajność JNA może być znacznie gorsza niż JNI. Zrobiłem bardzo prosty test, który nazwał prostą natywną funkcję zwiększającą liczbę całkowitą „return arg + 1;”. Benchmarki wykonane z jmh pokazały, że wywołania JNI do tej funkcji są 15 razy szybsze niż JNA.
Bardziej „złożony” przykład, w którym funkcja natywna sumuje tablicę liczb całkowitych składającą się z 4 wartości, nadal pokazuje, że wydajność JNI jest 3 razy szybsza niż JNA. Zmniejszona korzyść wynikała prawdopodobnie z tego, jak uzyskujesz dostęp do tablic w JNI: mój przykład utworzył kilka rzeczy i zwolnił je ponownie podczas każdej operacji sumowania.
Kod i wyniki testów można znaleźć na github .
Zbadałem JNI i JNA w celu porównania wydajności, ponieważ musieliśmy zdecydować, że jeden z nich wywoła bibliotekę dll w projekcie i mieliśmy ograniczenie czasu rzeczywistego. Wyniki pokazały, że JNI ma większą wydajność niż JNA (około 40 razy). Być może istnieje sztuczka zapewniająca lepszą wydajność w JNA, ale na prostym przykładzie jest bardzo powolna.
O ile czegoś nie brakuje, czy główna różnica między JNA a JNI jest taka, że z JNA nie można wywołać kodu Java z kodu natywnego (C)?
W mojej aplikacji JNI okazał się dużo łatwiejszy w użyciu. Musiałem czytać i zapisywać ciągłe strumienie do iz portu szeregowego - i nic więcej. Zamiast próbować nauczyć się bardzo skomplikowanej infrastruktury JNA, okazało się, że znacznie łatwiej było stworzyć prototyp natywnego interfejsu w systemie Windows za pomocą specjalnej biblioteki DLL, która wyeksportowała tylko sześć funkcji: