В разработке программного обеспечения Java Native Interface (JNI ) является внешней функцией интерфейс программирование фреймворк, который позволяет коду Java, работающему на виртуальной машине Java (JVM), вызывать и вызывать собственные приложения (программы, специфичные для аппаратное обеспечение и операционная система платформа) и библиотеки, написанные на других языках, таких как C, C ++ и сборка.
JNI позволяет программистам писать собственные методы для обработки ситуаций, когда приложение не может быть полностью написано на языке программирования Java, например когда стандартная библиотека Java class не поддерживает специфичные для платформы функции или программную библиотеку. Он также используется для изменения существующего приложения (написанного на другом языке программирования), чтобы оно стало доступным для приложений Java. Многие из стандартных библиотечных классов зависят от JNI для предоставления функциональности разработчику и пользователю, например файловый ввод-вывод и звуковые возможности. Включение реализаций API, зависящих от производительности и платформы, в стандартную библиотеку позволяет всем приложениям Java получать доступ к этой функции безопасным и независимым от платформы способом.
Среда JNI позволяет собственному методу использовать объекты Java так же, как код Java использует эти объекты. Собственный метод может создавать объекты Java, а затем проверять и использовать эти объекты для выполнения своих задач. Собственный метод также может проверять и использовать объекты, созданные кодом Java-приложения.
Только приложения и подписанные апплеты могут вызывать JNI.
Приложение, которое полагается на JNI, теряет платформенную переносимость, предлагаемую Java (частичный обходной путь - написать отдельную реализацию кода JNI для каждой платформы и заставить Java определять операционную систему и загружать правильную во время выполнения).
Не только собственный код может взаимодействовать с Java, он также может использовать Java Canvas
, что возможно с Java AWT Native Interface. Процесс почти такой же, с небольшими изменениями. Собственный интерфейс Java AWT доступен только начиная с J2SE 1.3.
JNI также обеспечивает прямой доступ к ассемблерному коду, даже не проходя через мост C. Таким же образом возможен доступ к приложениям Java из сборки.
В структуре JNI собственные функции реализованы в отдельных файлах.c или.cpp. (C ++ предоставляет несколько более простой интерфейс с JNI.) Когда JVM вызывает функцию, она передает указатель JNIEnv
, указатель jobject
и любые аргументы Java, объявленные методом Java. Например, следующее преобразует строку Java в собственную строку:
extern "C" JNIEXPORT void JNICALL Имя_класса_в_классе_MethodName (JNIEnv * env, jobject obj, jstring javaString) {const char * nativeString = env->GetStringUTFChars (javaStringUTFChars (javaStringUTFChars)); // Сделаем что-нибудь с nativeString env->ReleaseStringUTFChars (javaString, nativeString); }
Указатель env
- это структура, которая содержит интерфейс к JVM. Он включает в себя все функции, необходимые для взаимодействия с JVM и работы с объектами Java. Примеры функций JNI - это преобразование собственных массивов в / из массивов Java, преобразование собственных строк в / из строк Java, создание экземпляров объектов, выдача исключений и т. Д. По сути, все, что может делать код Java, можно сделать с помощью JNIEnv
, хотя и с гораздо меньшей легкостью.
Аргумент obj
- это ссылка на объект Java, внутри которого был объявлен этот собственный метод.
Собственные типы данных могут отображаться в / из типов данных Java. Для составных типов, таких как объекты, массивы и строки, машинный код должен явно преобразовать данные, вызывая методы в JNIEnv
.
указатель среды JNI (JNIEnv *) передается в качестве аргумента для каждой собственной функции, сопоставленной с методом Java, что позволяет взаимодействовать со средой JNI внутри собственного метода. Этот указатель интерфейса JNI может быть сохранен, но остается действительным только в текущем потоке. Другие потоки должны сначала вызвать AttachCurrentThread (), чтобы присоединиться к виртуальной машине и получить указатель интерфейса JNI. После присоединения собственный поток работает как обычный поток Java, работающий в собственном методе. Собственный поток остается подключенным к виртуальной машине до тех пор, пока он не вызовет DetachCurrentThread (), чтобы отсоединиться.
Инфраструктура JNI не обеспечивает никакой автоматической сборки мусора для ресурсов памяти, не относящихся к JVM, выделенных при выполнении кода. на родной стороне. Следовательно, собственный сторонний код (например, язык ассемблера) берет на себя ответственность за явное освобождение любых таких ресурсов памяти, которые получает собственный код.
На платформах Linux и Solaris, если собственный код регистрирует себя как обработчик сигналов, он может перехватывать сигналы, предназначенные для JVM. Цепочка ответственности может использоваться, чтобы позволить машинному коду лучше взаимодействовать с JVM. На платформах Windows Structured Exception Handling (SEH) может использоваться для обертывания собственного кода в блоках SEH try / catch, чтобы фиксировать программные прерывания, сгенерированные машиной (CPU / FPU) (например, нарушения доступа NULL-указателя и деления на ноль), и для обработки этих ситуаций до того, как прерывание будет передано обратно в JVM (т.е. побочный код Java), что, по всей вероятности, приведет к необработанному исключению.
Кодировка, используемая для Функции NewStringUTF, GetStringUTFLength, GetStringUTFChars, ReleaseStringUTFChars и GetStringUTFRegion - это «модифицированный UTF-8», который не является допустимым UTF-8 для всех входных данных, но на самом деле это другая кодировка. Нулевой символ (U + 0000) и кодовые точки не на базовой многоязычной плоскости (больше или равны U + 10000, т. Е. Те, которые представлены как суррогатные пары в UTF-16), кодируются по-разному в модифицированном UTF- 8. Многие программы на самом деле используют эти функции неправильно и обрабатывают строки UTF-8, возвращаемые или переданные в функции, как стандартные строки UTF-8, а не модифицированные строки UTF-8. Программы должны использовать функции NewString, GetStringLength, GetStringChars, ReleaseStringChars, GetStringRegion, GetStringCritical и ReleaseStringCritical, которые используют кодировку UTF-16LE в архитектурах с прямым порядком байтов и UTF-16BE в архитектурах с прямым порядком байтов, а затем использовать UTF-16 для UTF-16. 8.
В следующей таблице показано соответствие типов между Java (JNI) и машинным кодом.
Тип C | Тип языка Java | Описание | Сигнатура типа |
---|---|---|---|
unsigned char | jboolean | 8 бит без знака | Z |
подписанный char | jbyte | подписанный 8 бит | B |
беззнаковый короткий | jchar | беззнаковый 16 бит | C |
короткий | jshort | 16 бит со знаком | S |
long | jint | 32 бита со знаком | I |
long long. __int64 | jlong | 64 бита со знаком | J |
float | jfloat | 32 бита | F |
double | jdouble | 64 бита | D |
void | V |
In кроме того, подпись «L полностью-квалифицированный-класс;»
будет означать класс, однозначно указанный этим именем; например, подпись «Ljava / lang / String;»
относится к классу java.lang.String
. Кроме того, префикс [
к подписи делает массив этого типа; например, [I
означает тип массива int. Наконец, подпись void
использует код V
.
Эти типы взаимозаменяемы. Можно использовать jint
там, где вы обычно используете int
, и наоборот, без необходимости приведения типов. Однако сопоставление строк и массивов Java с собственными строками и массивами отличается. Если jstring
используется вместо char *
, код может вызвать сбой JVM.
JNI влечет за собой значительные накладные расходы и производительность потеря при определенных обстоятельствах:
Собственная реализация виртуальной машины Java от Microsoft (Visual J ++ ) имела аналогичный механизм для вызова собственного кода из Java, который называется Raw Native Interface (RNI). Кроме того, у него был простой способ вызвать существующий собственный код, который сам не знал о Java, такой как (но не ограничиваясь) Windows API, называемый J / Direct. Однако после судебного разбирательства Sun – Microsoft по поводу этой реализации Visual J ++ больше не поддерживается.
RNI было менее неуклюжим в использовании, чем JNI, потому что не требовалось учета с указателем среды Java. Вместо этого ко всем объектам Java можно было обращаться напрямую. Чтобы облегчить это, использовался инструмент, который генерировал файлы заголовков из классов Java. Точно так же J / Direct было легче использовать, чем использование необходимой промежуточной собственной библиотеки и JNI, хотя в настоящее время альтернативой является JNA.