2009-10-12 9 views
106

JNA wydaje się nieco łatwiejszy w użyciu do natywnego kodu w porównaniu do JNI. W jakich przypadkach używałbyś JNI zamiast JNA?Użyj JNI zamiast JNA do wywołania natywnego kodu?

+3

Wypróbuj także BridJ. –

+0

Jednym z ważnych czynników, które wybraliśmy jako JNA w porównaniu z JNI, jest to, że JNA nie wymagało żadnych modyfikacji naszych natywnych bibliotek. Konieczność dostosowania bibliotek rodzimych tylko po to, aby móc pracować z Javą, była dla nas dużym NIE. – kvr

Odpowiedz

119
  1. JNA nie obsługuje mapowanie klas C++, więc jeśli używasz C++ biblioteki trzeba otoki JNI
  2. Jeśli potrzebują dużo kopiowania pamięci. Na przykład wywołujesz jedną metodę, która zwraca ci duży bajtowy bufor, zmienisz coś w nim, musisz wywołać inną metodę, która używa tego bufora bajtowego. Wymagałoby to skopiowania tego bufora z c na java, a następnie skopiowania go z java do c. W tym przypadku jni wygrywa wydajność, ponieważ można przechowywać i modyfikować ten bufor wc, bez kopiowania.

Oto problemy, które napotkałem. Może jest coś więcej. Ale ogólnie wydajność nie różni się zbytnio od jny i jni, więc wszędzie, gdzie możesz korzystać z JNA, użyj go.

EDIT

Ta odpowiedź wydaje się być dość popularne. Więc oto kilka dodatków:

  1. Jeśli trzeba Mapa C++ lub COM, istnieje biblioteka Oliver Chafic, twórca JNAerator, zwane BridJ. Jest jeszcze młody biblioteki, ale ma wiele ciekawych funkcji:
    • dynamiczne współdziałanie C/C++/COM: wywoływanie metod C++ tworzenie C++ obiektów (i podklasy C++ klas z Javy!)
    • proste mapowania typu z dobre wykorzystanie leków generycznych (w tym wiele ładniejszy model Wskaźniki) wsparcie
    • Pełna JNAerator
    • działa na Windows, Linux, MacOS X, Solaris, Androida
  2. Co do kopiowania pamięci, wierzę JNA obsługuje bezpośrednie ByteBuffers , więc pamięć c opying można uniknąć.

Nadal uważam, że w miarę możliwości lepiej jest używać JNA lub BridJ i powracać do jni, jeśli wydajność jest krytyczna, ponieważ jeśli często trzeba wywoływać funkcje natywne, zauważalny jest spadek wydajności.

+6

Nie zgadzam się, JNA ma dużo na głowie. Choć wygoda jest warta użycia w nie krytycznym kodzie czasowym –

+3

, radzę zachować ostrożność przed użyciem BridJ dla projektu Android, cytując bezpośrednio z jego [strony pobierania] (https://code.google.com/p/bridj/wiki/Pobierz # Android): "BridJ działa częściowo na emulatory Android/ramię (z SDK) i __prawdopodobnie nawet na rzeczywistych urządzeniach (untested) .__" – kizzx2

+0

dobrze używam jna i jak denis powiedział, że nazywam jedną metodę, która zwraca mi dane bufora dużego bajtu obrazu z urządzenia USB. Ale w moim przypadku metoda zależy od zewnętrznego aparatu, więc dopóki ktoś nie zrobi zdjęcia, musi poczekać wewnątrz tej biblioteki. Ogólnie rzecz biorąc działa to dobrze, ale w wielu innych komputerach niż mój automatycznie powraca z metody przyjmowania pustych danych bez czekania nawet na sekundę. Stawiłem czoła tym problemom na starszych komputerach i tym z usb 3.0. Czy to jest problem jna? – Jony

5

Nie jest to bezpośrednia odpowiedź i mam żadnego doświadczenia z JNA, ale kiedy patrzę na Projects Using JNA i zobaczyć nazwy jak SVNKit, IntelliJ IDEA, NetBeans IDE, etc, jestem skłonni wierzyć, że jest to całkiem przyzwoity biblioteka.

Właściwie, sądzę, że użyłbym JNA zamiast JNI, gdybym musiał, ponieważ wygląda na to, że jest prostszy niż JNI (który ma nudny proces rozwoju). Szkoda, JNA nie została wydana w tym czasie.

8
  1. Piszesz kod kilka lat temu, zanim pojawił się JNA lub celujesz w wersję 1.4 JRE.
  2. Kod, nad którym pracujesz, nie znajduje się w katalogu DLL \ SO.
  3. Pracujesz nad kodem niezgodnym z LGPL.

To tylko to, co mogę wymyślić z góry mojej głowy, choć nie jestem zbyt dużym użytkownikiem. Wydaje się również, że możesz uniknąć JNA, jeśli potrzebujesz lepszego interfejsu niż ten, który oferujesz, ale możesz kodować w ten sposób w java.

+9

Nie zgadzam się z 2 - konwertowanie statycznej lib na dynamiczną bibliotekę jest łatwe. Zobacz moje pytanie abput to http://stackoverflow.com/questions/845183/convert-static-windows-library-to-dll –

+1

Począwszy od JNA 4.0, JNA jest podwójnie licencjonowana zarówno na licencji LGPL 2.1, jak i Apache 2.0, to zależy od ciebie – sbarber2

28

Trudno jest odpowiedzieć na tak ogólne pytanie. Przypuszczam, że najbardziej oczywistą różnicą jest to, że w przypadku JNI konwersja typu jest implementowana po natywnej stronie granicy Java/native, natomiast w przypadku JNA konwersja typu jest realizowana w Javie. Jeśli już czujesz się całkiem komfortowo z programowaniem w C i musisz implementować kod natywny sam, zakładam, że JNI nie będzie zbyt skomplikowany. Jeśli jesteś programistą Java i potrzebujesz tylko wywołać bibliotekę natywną innej firmy, używanie JNA jest prawdopodobnie najłatwiejszą drogą do uniknięcia być może nie tak oczywistych problemów z JNI.

Chociaż nigdy nie porównywałem żadnych różnic, chciałbym, ze względu na projekt, przynajmniej przypuszczam, że konwersja typu z JNA w niektórych sytuacjach będzie działać gorzej niż z JNI. Na przykład przy przekazywaniu tablic JNA konwertuje je z 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, gdy generowany jest macierzysty "widok" tablicy, potencjalnie tylko tworząc widok części tablicy, utrzymując widok przez kilka wywołań funkcji, a na końcu zwolnij widok i zdecyduj, czy chcesz aby zachować zmiany (ewentualnie wymagające skopiowania danych) lub odrzucić zmiany (nie jest wymagana kopia). Wiem, że możesz używać natywnej macierzy do wywoływania funkcji za pomocą JNA przy użyciu klasy Memory, ale będzie to również wymagać kopiowania pamięci, co może być niepotrzebne w przypadku JNI. Różnica może nie być istotna, ale jeśli Twoim pierwotnym celem jest zwiększenie wydajności aplikacji poprzez implementację jej części w natywnym kodzie, użycie słabszej technologii mostu nie wydaje się najbardziej oczywistym wyborem.

1

O ile czegoś mi nie brakuje, czy główna różnica między JNA a JNI nie jest taka, że ​​w przypadku JNA nie można wywoływać kodu Java z natywnego (C) kodu?

+6

Możesz. Z klasą zwrotną, która odpowiada wskaźnikowi funkcji po stronie C. void register_callback (void (*) (const int)); byłby zmapowany do publicznego statycznego natywnego void register_callback (MyCallback arg1); gdzie MyCallback jest interfejsem rozszerzającym com.sun.jna.Callback z pojedynczą metodą void apply (int value); –

+0

@Augusto może to być komentarz również proszę – swiftBoy

7

Nawiasem mówiąc, w jednym z naszych projektów trzymaliśmy bardzo mały odcisk stopy JNI. Użyliśmy buforów protokołów do reprezentowania naszych obiektów domenowych i dlatego mamy tylko jedną natywną funkcję do mostkowania Javy i C (wtedy oczywiście funkcja C będzie wywoływać kilka innych funkcji).

+4

Tak więc, zamiast wywołania metody, przekazujemy wiadomość. Zainwestowałem sporo czasu w JNI i JNA, a także w BridJ, ale dość szybko wszystkie one są trochę zbyt przerażające. –

2

Przeanalizowałem JNI i JNA pod kątem porównania wydajności, ponieważ musieliśmy zdecydować, aby jeden z nich wywoływał bibliotekę DLL w projekcie i mieliśmy ograniczenia w czasie rzeczywistym. Wyniki pokazały, że JNI ma większą wydajność niż JNA (około 40 razy). Może istnieje podstęp dla lepszej wydajności w JNA, ale jest bardzo powolny na prostym przykładzie.

3

Jeśli chcesz uzyskać wydajność JNI, ale jesteś zniechęcony jego złożonością, możesz rozważyć użycie narzędzi generujących powiązania JNI automatycznie. Na przykład: JANET (zrzeczenie: napisałem to) pozwala na miksowanie kodu Java i C++ w jednym pliku źródłowym, a np. wykonywać połączenia z C++ na Javę, używając standardowej składni Java. Na przykład, oto jak chcesz wydrukować ciąg C do standardowego wyjścia Java:

native "C++" void printHello() { 
    const char* helloWorld = "Hello, World!"; 
    `System.out.println(#$(helloWorld));` 
} 

JANET następnie przekłada Java grawis zatopionych w odpowiednim JNI nazywa.

1

Rzeczywiście zrobiłem kilka prostych testów porównawczych z JNI i JNA.

Jak już inni zwrócili uwagę, JNA jest dla wygody. Nie musisz kompilować ani pisać natywnego kodu podczas korzystania z JNA. Natywny program ładujący biblioteki JNA jest również jednym z najlepszych/najłatwiejszych w użyciu, jakie kiedykolwiek widziałem. Niestety, nie można go używać do JNI. (Właśnie dlatego napisałem an alternative for System.loadLibrary(), który używa konwencji ścieżki JNA i obsługuje płynne ładowanie ze ścieżki klasy (np. Słoiki).)

Wydajność JNA może być jednak dużo gorsza niż w przypadku JNI. Zrobiłem bardzo prosty test, który nazwał prostą natywną funkcję przyrostu liczby całkowitej "return arg + 1;". Testy porównawcze wykonane za pomocą jmh wykazały, że wywołania JNI do tej funkcji są 15 razy szybsze niż w przypadku JNA.

Bardziej "złożony" przykład, w którym natywna funkcja podsumowuje tablicę liczb całkowitych z 4 wartościami, wciąż pokazuje, że wydajność JNI jest 3 razy większa niż w przypadku JNA. Zmniejszona przewaga wynikała prawdopodobnie z tego, jak uzyskujesz dostęp do tablic w JNI: mój przykład tworzył pewne rzeczy i wypuszczał je ponownie podczas każdej operacji podsumowania.

Kod i wyniki testu można znaleźć at github.

Powiązane problemy