Natywny interfejs Java
Java Native Interface ( JNI ) to struktura programistyczna, która umożliwia programowi Java działającemu w wirtualnej maszynie Java (JVM) interakcję z programami napisanymi w innych językach, takich jak C , C++ i asembler .
Cel i charakterystyka
JNI służy do pisania metod natywnych w celu przezwyciężenia sytuacji, w których aplikacji nie można napisać w całości w języku Java , na przykład gdy standardowa biblioteka klas nie zapewnia obsługi funkcji zależnych od platformy.
Służy również do modyfikowania istniejących programów napisanych w innym języku, umożliwiając dostęp do nich z aplikacji Java. Wiele standardowych klas Java API zależy od JNI, aby zapewnić funkcjonalność programiście i użytkownikowi, na przykład funkcjonalność odczytu/zapisu dźwięku lub pliku. Deweloper musi upewnić się, że standardowe API Java nie zapewnia określonej funkcjonalności przed skorzystaniem z JNI, ponieważ ten pierwszy oferuje bezpieczną i niezależną od platformy implementację.
Framework JNI umożliwia rodzimej metodzie używanie obiektów Java w taki sam sposób, jak robi to sam kod Java. Metoda natywna może tworzyć obiekty Java; badać je i wykorzystywać do pełnienia ich funkcji. Metoda natywna może również badać i wykorzystywać obiekty utworzone przez kod aplikacji napisany w Javie.
JNI jest często określany jako „zawór ucieczkowy” dla programistów, ponieważ umożliwia im dodawanie funkcjonalności do swoich aplikacji, których Java API nie może zapewnić.
Ponieważ - jak zostało powiedziane wcześniej - może być używany do interakcji z kodem napisanym w innych językach, takich jak C++, jest również używany do operacji i obliczeń o dużej złożoności czasowej, ponieważ kod natywny jest generalnie szybszy niż ten, który jest wykonywany na maszynie wirtualnej.
Ostrzeżenia
JNI wcale nie jest trywialne, zaleca się, aby korzystali z niego doświadczeni programiści. W każdym razie możliwość komunikowania się z Javą za pomocą C, C++ lub asemblera usuwa wszelkie ograniczenia możliwości programów Java.
Rozważając użycie JNI, warto pamiętać o następujących kwestiach:
- JNI nie jest łatwym do nauczenia interfejsem API .
- Drobne błędy w korzystaniu z JNI mogą całkowicie zdestabilizować wirtualną maszynę Javy w sposób, który jest bardzo trudny do odtworzenia i naprawienia.
- Tylko podpisane aplikacje i aplety mogą wywoływać JNI.
- Aplikacja, która powraca do JNI, traci jedną z najważniejszych cech, jakie daje jej Java, czyli swoją przenośność. (Jednym ze sposobów obejścia tego jest napisanie oddzielnej implementacji kodu JNI dla każdej platformy i poproszenie Javy o wykrycie systemu operacyjnego, aby uruchomić jedną lub drugą implementację, gdy nadejdzie czas).
- Po stronie JNI nie ma wyrzucania elementów bezużytecznych (kod JNI musi jawnie zdelokalizować swoje wskaźniki).
Automatyczny garbage collector w Javie różni się nieco od malloc/free w C, ponieważ może przenosić obiekty po przydzieleniu im niezbędnej pamięci. Dlatego bardzo ważne jest, aby wskaźniki do obiektów Java były uzyskiwane i poprawnie blokowane. Programiści przyzwyczajeni do C często tego nie rozumieją, co może prowadzić do dość ezoterycznych i nieodwracalnych błędów.
We wszystkich powyższych przypadkach JNI powinno być używane z ostrożnością i często jest unikane przez programistów Java. Na przykład większość baz danych JDBC komunikuje się bezpośrednio z gniazdem zamiast korzystać z istniejących interfejsów API języka C.
Jak działa JNI
W JNI natywne funkcje są zaimplementowane w oddzielnych plikach .c lub .cpp (C++ oferuje nieco bardziej przejrzysty interfejs z JNI). Gdy maszyna wirtualna wywołuje funkcję, przekazuje jej wskaźnik do JNIEnv, wskaźnik do jobjecti dowolną liczbę argumentów zadeklarowanych przez metodę Java. Funkcja JNI powinna wyglądać mniej więcej tak:
JNIEXPORT void JNICALL Java_NazwaKlasy_NazwaMetody
( JNIEnv * env , obiekt zadania )
{
//Natywna metoda jest zaimplementowana tutaj
}
Wskaźnik JNIEnv *envto struktura zawierająca interfejs do maszyny wirtualnej. Zawiera wszystkie niezbędne funkcje do interakcji z JVM (Java Virtual Machine) i do pracy z obiektami Java.
Jako przykłady użycia tego interfejsu możemy wymienić konwersję wektorów (styl C) do/z wektorów Javy lub natywnych stringów (wskaźniki do znaków) z/na stringi Javy (obiekty String); tworzenie instancji obiektów, rzucanie i łapanie wyjątków itp.
W istocie, użycie JNIEnvmoże zrobić wszystko, co może zrobić kod Java; tak, ze znacznie zwiększoną trudnością.
Na przykład poniższy fragment kodu konwertuje ciąg Java na ciąg natywny.
//Kod C++
JNIEXPORT void JNICALL Java_ClassName_MethodName
( JNIEnv * env , jobject obj , jstring javaString )
{
//Pobierz natywny ciąg z łańcucha java
const char * nativeString = env -> GetStringUTFChars ( javaString , 0 );
//zrób coś z tym
/* NIE ZAPOMNIJ O TEJ linijce, zwalnia to miejsce zajmowane przez użyty ciąg
* ma to związek ze sposobem, w jaki Java obsługuje ciągi */
env -> ReleaseStringUTFChars ( javaString , nativeString );
}
//Kod C
JNIEXPORT void JNICALL Java_ClassName_MethodName
( JNIEnv * env , jobject obj , jstring javaString )
{
//Pobierz natywny ciąg z java string
const char * nativeString = ( * env ) -> GetStringUTFChars ( env , javaString , 0 );
//zrób coś z tym
/* NIE ZAPOMNIJ O TEJ linijce, zwalnia to miejsce zajmowane przez użyty ciąg
* ma to związek ze sposobem, w jaki Java obsługuje ciągi */
( * env ) -> ReleaseStringUTFChars ( env , javaString , nativeString );
}
Zauważ, że kod C++ JNI jest bardziej czytelny składniowo niż kod C, ponieważ podobnie jak Java, C++ używa semantyki wywołania metod zorientowanych obiektowo,
Oznacza to, że w C parametr envmusi być wyłuskany przy użyciu (*env)i musi być jawnie przekazany (pojawia się jako parametr) do metod JNIEnv.
W C++ parametr envjest wyłuskiwany przy użyciu env->i przekazywany niejawnie jako część semantyki wywołania metody obiektowej (nie pojawia się jako parametr, ponieważ metoda jest częścią środowiska env, gdy jest traktowana jako obiekt).
Natywne typy danych mogą podlegać konwersji do/z typów danych Java. W przypadku typów złożonych, takich jak objects, arrays i strings , metoda natywna musi przekonwertować dane, jawnie wywołując metody w JNIEnv(Java Native Interface Environment).
Bibliografia
- ShengLiang (1999). Java Native Interface: Przewodnik programisty i specyfikacja . wersja pdf . Addisona-Wesleya. ISBN 0-201-32577-2 .