1спредприятие — Вызов метода Java из кода C++


Содержание

Вызов функции C ++ из Java

Я работаю над проектом (на Java), в котором мне нужно оценить пользовательские коды, которые могут быть написаны на C ++ или Java. Чтобы оценить код, мне нужно передать параметр в код и получить из него возвращаемое значение. Я могу легко сделать это для кодов Java; просто создайте объект для класса пользователя и затем вызовите функцию, используя объект. У меня проблемы с этим для C ++.

Какие возможные методы я могу вызвать функцию C ++ из Java и получить ее возвращаемое значение? Мой код выглядит примерно так (я не могу дать точный код здесь).
Код C ++

Java-код, откуда вызывается вышеуказанная функция

Я прочитал в Интернете, что JNI можно использовать, но он НЕ позволяет мне использовать существующий код, он просит изменить массивы int на jint. Есть ли способ, которым я могу достичь своей цели? Я просто хочу передать массив в существующий код C ++ и получить его возвращаемое значение.

Решение

После компиляции используйте javah создать свой собственный заголовочный файл

javah -classpath test.PerformWork

Наконец, реализовать нативную функцию

Другие решения

Вам нужно Java Native Interface или же JNI ,

Человек, задающий вопрос, явно не может связать свой исполняемый файл Java. И не хотел бы делать это с кодом, который был представлен пользователями для проверки, так как этот код может легко привести к сбою jvm.

Кажется, что правильный ответ — скомпилировать предоставленный пользователем код в файл .o, а затем связать его с предварительно написанной «основной» программой, которая вызывает предварительно определенное имя метода, передавая любые наборы параметров, необходимые для тестирования и оценки. Этот автономный собственный исполняемый файл может быть легко вызван из Java-приложения, а результаты возвращены пользователю.

Интеграция кода c++ в java

15.05.2013, 18:55

Конвертация кода из с++ в java
Доброго времени суток. Перейду сразу к сути. Я затеял перевод кода из c++ в java, код простой.

Перевод кода с Java на С++
Помогите перевести следующий код с Java на C++: import java.io.File; import.

Перевод кода с Java на С++
Помогите перевести следующий код с Java на C++: * Вызывающий класс*/ public class Main<.

Конвертация кода из С++ в Java
Помогите пожалуйста с переводом кода из с++ в Java. В Java не разбираюсь к сожалению. Заранее.

C++ и Java: совместное использование

Глубинное родство этих языков программирования позволяет им взаимодействовать, расширяя возможности каждого.

Язык JAVA во многом произошел от С/С++, у которых были позаимствованы синтаксис и базовая семантика. Однако связь между ними не ограничивается только этим. Используя JNI (JAVA NATIVE INTERFACE), можно вызывать С/С++-функции из JAVA-программы и, наоборот, из программы, написанной на С/С++, можно создавать JAVA-объекты и вызывать JAVA-методы. Несмотря на то, что использование JNI в большинстве случаев ведет к потере многоплатформенности JAVA-кода, данная возможность расширяет сферу применения самого языка JAVA на приложения, для которых это условие не является необходимым. В таких системах использование JNI позволяет сочетать современный объектно-ориентированный подход JAVA — главное преимущество этой технологии, с существующим (LEGACY) системно-зависимым (PLATFORM SPECIFIC) кодом на С/С++. Это является важным и необходимым условием перехода к использованию JAVA-технологии при разработке компонентов сервера.

Существует несколько причин совместного использования С/С++ и JAVA: стандартные библиотеки JAVA-классов не всегда поддерживают некоторые системно-зависимые возможности; необходимость использования наработанного и отлаженного кода на других языках или желание максимально эффективно реализовать участок кода, критичного с точки зрения времени исполнения. Эти причины не существенны при разработке клиентских приложений, однако в случае серверных — они становятся доминирующими.

Для обеспечения интероперабельности программного кода в рамках С/С++ и JAVA JDK1.1 (JAVA DEVELOPERS KIT) предоставляет набор интерфейсов, объединенных в JNI (JAVA NATIVE INTERFACE). JNI позволяет JAVA-коду, исполняемому виртуальной JAVA-машиной (JVM — JAVA VIRTUAL MACHINE), взаимодействовать с приложениями и библиотеками, написанными на языках С/С++ или Ассемблера.

Основным преимуществом JNI перед предыдущей версией (JDK 1.0 NI — NATIVE INTERFACE) и другими сходными интерфейсами (NETSCAPE JAVA RINTIME INTERFACE или MICROSOFT’S RAW NATIVE INTERFACE AND COM/JAVA INTERFACE) является то, что JNI изначально разрабатывался для обеспечения двоичной совместимости (BINARY COMPATIBILITY), подразумевающей совместимость приложений, написанных с использованием JNI, для любых JVM на конкретной платформе. Другими словами, один и тот же скомпилированный С/С++-код должен одинаково корректно исполняться JVM NETSCAPE NAVIGATOR и MICROSOFT EXPLORER, SYMANTEC VISUAL CAFО и SUN JAVA WORKSHOP и т.д. для данной платформы (WIN32). Следует отметить, что ранние интерфейсы не удовлетворяли этому условию. Например, JDK 1.0 NI, входящий в JDK 1.0.2 и поддерживаемый в JDK 1.1 для обратной совместимости, использует С-структуры для доступа к членам JAVA-объекта, что определяет зависимость С/С++-кода от того, как JVM располагает объекты в памяти. В общем случае, при использовании JDK 1.0 NI требуется перекомпиляция соответствующего С/С++-кода для каждой JVM на данной платформе.

Несмотря на определенную универсальность интерфейса, обусловленную его двоичной совместимостью, JNI обладает широкой функциональностью, предоставляя разработчику все низкоуровневые механизмы JVM: создание JAVA-объектов, включая создание массивов и объектов типа STRING; вызов JAVA-методов; возбуждение и перехват исключительных ситуаций (EXCEPTION); загрузка JAVA-классов и динамический анализ типа (RUNTIME TYPE CHECKING). Отдельно в JNI входит INVOCATION API, позволяющий приложениям динамически загружать JVM. Динамическая загрузка JVM из С/С++-кода позволяет легко встраивать возможности JAVA в существующие системы без необходимости их статического связывания (LINKAGE) с кодом JVM.

Ниже будет рассмотрено, как создавать коды на С/С++ и JAVA для их совместного использования в рамках JNI и INVOCATION API. Все примеры разработаны и протестированы на платформе WINDOWS 95. Во всех случаях, когда это необходимо, даются пояснения для платформы UNIX.

JNI определяется библиотечными и заголовочными (HEADER) файлами для С/С++. Библиотечные файлы хранятся в подкаталоге LIB (DLL — DYNAMIC-LINK LIBRARY, для WIN32 — в подкаталоге BIN), а заголовочные файлы — в подкаталоге INCLUDE основного каталога JAVA.

Использование JNI


Взаимодействие кодов JAVA и С/С++ может осуществляться двумя способами: С/С++-код получает управление непосредственно из JAVA-программы путем вызова собственного (NATIVE) метода; С/С++-код динамически загружает JVM с помощью INVOCATION API. Во втором случае, по сути, реализуется специализированная JVM, так как разработчик С/С++-кода сам решает, в какой последовательности выполнять JAVA-код (когда и какие JAVA-объекты создавать, какие методы вызывать и т. д.).

Рассмотрим первую из указанных возможностей.

Для того чтобы передать управление С/С++-коду из JAVA-программы, необходимо создать собственный JAVA-метод, сгенерировать с помощью утилиты JAVAH заголовочный файл для С/С++-функций, разработать сами функции, в которые будет передаваться управление, и оттранслировать их, поместив в библиотечный файл. После создания библиотеки ее можно загружать из JAVA-программы для последующего вызова собственных методов.

Создание собственного JAVA-метода

Собственный метод создается путем добавления к его описанию спецификатора NATIVE, при этом он не должен иметь реализации (так же как и методы в описании интерфейса). Спецификатор NATIVE сообщает компилятору, что реализация данного метода будет представлена в виде откомпилированного С/С++-кода, помещенного в библиотечный файл. Когда JVM встречает обращение к собственному методу, происходит вызов соответствующей С/С++-функции. Помимо описания собственнного метода, JAVA-код должен динамически загрузить библиотеку, содержащую С/С++-функцию с реализацией данного метода. Для этого в классе JAVA.LANG.SYSTEM существует метод PUBLIC STATIC VOID LOADLIBRARY (STRING LIBNAME), загружающий указанную библиотеку. Следующий пример демонстрирует описание собственного метода.

CLASS SYSTEMSPECIFIC <
STATIC <
SYSTEM.LOADLIBRARY(«SYSSPEC»);
>
NATIVE VOID DOSPECIFIC();
>

В приведенном примере метод DOSPECIFIC() является собственным, и его С/С++-реализация находится в библиотеке SYSSPEC. Метод LOADLIBRARY() вызывается в статическом инициализаторе, что обеспечивает единственный вызов этого метода после загрузки класса SYSTEMSPECIFIC загрузчиком классов ( CLASS LOADER ). В принципе, LOADLIBRARY() можно вызывать более одного раза (например, в конструкторе), однако загрузка библиотеки будет происходить только при первом обращении к LOADLIBRARY() , поскольку при последующих вызовах этого метода определяется, что библиотека уже загружена и будет просто возвращаться управление.

Метод LOADLIBRARY() преобразует свой параметр в соответствии с тем, как именуются библиотечные файлы на конкретной платформе. В данном примере SYSSPEC преобразуется в SYSSPEC.DLL и LIBSYSSPEC.SO для WIN32 и UNIX соответственно. Метод LOADLIBRARY() использует стандартный алгоритм поиска библиотеки для данной платформы. Для WIN32 DLL должна находиться либо в текущем каталоге процесса, либо в каталоге, содержащем EXE-файл, то есть исполняемый модуль JVM, находящийся в подкаталоге BIN основного каталога JAVA, либо в системном каталоге WIN32, либо каталоге WINDOWS или в каталогах, указанных в переменной окружения PATH . Для UNIX библиотечный файл должен находиться либо в текущем каталоге процесса, либо в подкаталоге LIB основного каталога JAVA, либо в каталогах, перечисленных в переменной окружения LD_LIBRARY_PATH . Если указанную библиотеку найти не удается, метод LOADLIBRARY() генерирует исключительную ситуацию JAVA.LANG.UNSATISFIEDLINKERROR . Однако данная ситуация возникает не только в этом случае. Когда интерпретатор встречает вызов собственного метода, он ищет его (точнее его полную сигнатуру) в списке методов загруженных библиотек. Если метод не найден, то генерируется указанная исключительная ситуация.

Для более надежной работы с собственными методами можно использовать, к примеру, следующий код:

PUBLIC CLASS APP <
PUBLIC STATIC VOID MAIN(STRING ARGS) <
SYSTEMSPECIFIC SS = NEW SYSTEMSPECIFIC();
TRY <
SS.DOSPECIFIC();
>
CATCH (UNSATISFIEDLINKERROR E) <
SYSTEM.OUT.PRINTLN(«метод не найден (» + E + «)»);
>
>
>
CLASS SYSTEMSPECIFIC <
STATIC <
TRY <
SYSTEM.LOADLIBRARY(«SYSSPEC»);
>
CATCH (UNSATISFIEDLINKERROR E) <
SYSTEM.OUT.PRINTLN(«библиотека не найдена (» + E + «)»);
>
>
NATIVE VOID DOSPECIFIC();
>

Компиляция программ, содержащих собственные методы, ничем не отличается от компиляции обычных программ. Например, если записать предыдущий пример в файл с именем APP.JAVA, то для его компиляции необходимо выполнить следующую команду:

C:\ JAVAC APP.JAVA

Создание заголовочного файла

Создание С/С++-кода необходимо начинать с создания заголовочного файла. Его можно написать вручную или воспользоваться утилитой JAVAH. Второй путь предпочтительней, так как допускает меньшее количество ошибок. При обращении к утилите JAVAH указывается имя класса и параметр -JNI. Без него JAVAH будет генерировать файл в формате JDK 1.0 NI. Имя класса представляет собой полное квалифицированное имя класса. Например:

JAVAH -JNI JAVA.LANG.RUNTIME

Перед использованием утилиты JAVAH соответствующий JAVA-класс должен быть скомпилирован в CLASS-файл. Утилита JAVAH анализирует CLASS-файл и строит заголовочный файл, в котором перечислены объявления С/С++-функций, представляющих реализации соответствующих собственных методов. В качестве имен создаваемых заголовочных файлов используются полные квалифицированные имена классов, которые описаны в указанном файле и содержат собственные методы. Например, если выполнить следующие команды:

JAVAC APP.JAVA
JAVAH -JNI SYSTEMSPECIFIC

то JAVAH сгенерирует следующий файл SYSTEMSPECIFIC.H:

/* DO NOT EDIT THIS FILE — IT IS MACHINE GENERATED */
#INCLUDE <JNI.H>
/* HEADER FOR CLASS SYSTEMSPECIFIC */
#IFNDEF _INCLUDED_SYSTEMSPECIFIC
#DEFINE _INCLUDED_SYSTEMSPECIFIC
#IFDEF _ _CPLUSPLUS
EXTERN «C» <
#ENDIF
/*
* CLASS: SYSTEMSPECIFIC
* METHOD: DOSPECIFIC
* SIGNATURE: ()V
*/
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV *, JOBJECT);
#IFDEF _ _CPLUSPLUS
>
#ENDIF
#ENDIF

Цукерберг рекомендует:  Вакансии BrokenWorld

Как указывалось выше, данный файл можно создать вручную или с помощью утилиты JAVAH. В последнем случае не рекомендуется вносить в него какие-либо изменения, так как при последующем применении JAVAH к данному классу все внесенные изменения будут потеряны.

Директива препроцессора #INCLUDE <JNI.H> включает файл JNI.H (из подкаталога INLCUDE основного каталога JAVA), в котором находятся все необходимые объявления типов и функций для реализации собственного метода.

Макросы JNIEXPORT и JNICALL необходимы только для платформы WIN32, где они раскрываются соответственно в __DECLSPEC(DLLEXPORT) и __STDCALL и позволяют более эффективно строить DLL. Платформа UNIX использует для этих целей обычные С-соглашения, поэтому указанные макросы раскрываются в пустые строки.

Как видно из примера, имя С/С++-функции значительно отличается от имени собственного JAVA-метода. Важным понятием при построении имени С/С++-функции и использовании JNI-функций является сигнатура метода (SIGNATURE или METHOD ARGUMENTS SIGNATURE).

Сигнатура метода

Сигнатура метода — это сокращенная форма записи параметров метода и типов возвращаемого значения. Следует подчеркнуть, что в сигнатуру не входят ни имя метода, ни имена параметров. JNI формирует сигнатуры в соответствии с правилами, представленными в табл. 1.

Знак сигнатуры JAVA-тип
Z BOOLEAN
B BYTE
C CHAR
S SHORT
Internet INT
J LONG
F FLOAT
V VOID
D DOUBLE
L полное квалифицированное имя класса полное квалифицированное имя класса
[ тип тип[]
(типы аргументов) возвращаемый тип полная сигнатура метода

Проиллюстрируем эти правила на примерах:

  • метод LONG M1(INT N, STRING S, INT[] ARR) ;
  • сигнатура (ILJAVA/LANG/STRING;[I)J ;
  • метод VOID M2(FLOAT N, BYTE[][] ARR, RUNTIME R) ;
  • сигнатура (F[[BLJAVA/LANG/RUNTIME;)V .


Полная информация о правилах образования сигнатуры метода представлена в файле SIGNATURE.H.

Правила формирования имени С/С++-функции

Имя С/С++-функции формируется путем последовательного соединения следующих компонентов:

  • префикс JAVA_ ;
  • полное квалифицированное имя класса;
  • символ подчеркивания («_»);
  • имя метода;
  • для перегружаемых (OVERLOADED) методов — два символа подчеркивания («_ _») с последующей сигнатурой метода.

Использование имен с сигнатурой на конце необходимо только в случае перегрузки двух или более собственных методов (перегрузка с обычным методом не важна, так как обычные методы не будут находиться в создаваемой библиотеке, что, однако, не допускает наличия собственного и обычного метода с одинаковыми именами и сигнатурами).

Для соответствия лексиграфическим правилам С/С++ и использования UNICODE-кодировки, применяются дополнительные правила преобразования, представленные в табл. 2.

Исходный символ Результирующая последовательность
«_» _1
«;» _2
«[« _3
символ UNICODE с кодом ХХХХ _0ХХХХ

Ниже приведен пример JAVA-класса с собственными методами:

PACKAGE TESTPACKAGE;
ABSTRACT CLASS TEST <
PUBLIC NATIVE VOID M1(STRING[] SA, OBJECT O, INT[][] IA2);
PUBLIC NATIVE FLOAT[] M1(DOUBLE D, TEST T);
PUBLIC NATIVE TEST M3(INT I);
>

и соответствующие им имена С/С++-функций:

JNIEXPORT VOID JNICALL JAVA_TESTPACKAGE_TEST_M1___3LJAVA_LANG_STRING_2LJAVA_LANG_OBJECT_2_3_3I
(JNIENV *, JOBJECT, JOBJECTARRAY, JOBJECT, JOBJECTARRAY);
JNIEXPORT JFLOATARRAY JNICALL JAVA_TESTPACKAGE_TEST_M1__LJAVA_LANG_DOUBLE_2LTESTPACKAGE_TEST_2
(JNIENV *, JOBJECT, JOBJECT, JOBJECT);
JNIEXPORT JOBJECT JNICALL JAVA_TESTPACKAGE_TEST_M3
(JNIENV *, JOBJECT, JINT);

Рассмотрим типы параметров, которые получает на входе С/С++-функция при ее вызове.

Типы и структуры данных JNI

JNI использует целый набор типов для своих функций и для формальных параметров С/С++-функций, представляющих реализацию собственных методов. Все эти типы описаны в файле JNI.H, который включается в любой заголовочный файл для JNI. Файл JNI.H использует стандартную технику препроцессирования с макросом _CPLUSPLUS. Тем самым, в зависимости от того, какой (С++ или С) код компилируется, будут создаваться две немного отличающиеся версии описания типов. Каждая из них требует определенного синтаксиса доступа.

Файл JNI_MD.H содержит системно-зависимые описания JINT, JLONG и JBYTE. В этом же файле определены макросы JNIEXPORT и JNICALL. Тип VOID используется без переопределения.

Следует отметить, что для представления строковых объектов JNI использует сокращенный вариант формата UTF-8.

Первым аргументом С/С++-функции, представляющей реализацию собственного метода, является указатель на структуру JNIENV. Смысл этого указателя определяет важную идею, лежащую в основе реализации JNI. Если отвлечься от конкретного языка, то указатель на JNIENV, или интерфейсный указатель (JNI INTERFACE POINTER), является указателем на массив указателей, каждый из которых указывает на прикладную функцию JNI. Только через этот указатель С/С++-функция может получить доступ к функциям и ресурсам JNI. В случае С++ (макрос _CPLUSPLUS определен) тип JNIENV является структурой, а в случае С — указателем на структуру. В силу этого, для доступа к функциям JNI в С и С++ применяется различный синтаксис:

// C
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV* ENV, JOBJECT THIS) <
JINT VERSION = (*ENV)->GETVERSION(ENV);
Е
>
// C++
JNIEXPORT VOID JNICALL JAVA_SYSTEMSPECIFIC_DOSPECIFIC(JNIENV* ENV, JOBJECT THIS) <
JINT VERSION = ENV->GETVERSION();
Е
>

Главным преимуществом такой организации функций JNI является легкость модификации и дальнейшего расширения интерфейса.

Указатель на JNIENV действителен только в текущем потоке (THREAD). JVM гарантирует передачу одного и того же интерфейсного указателя всем методам, вызываемым из данного потока. Тем самым запрещено передавать интерфейсный указатель другому потоку. Если методы вызываются из разных потоков, то в этом случае каждый метод получает различные интерфейсные указатели.

Если С/С++-функция представляет реализацию нестатического собственного метода, то вторым параметром функции является объект типа JOBJECT. Данный параметр является ссылкой на JAVA-объект, для которого был вызван соответствующий собственный метод. Если функция представляет статический собственный метод, то вторым параметром является объект типа JCLASS, определяющий JAVA-класс, для которого вызван собственный метод класса (CLASS METHOD).

Последующие параметры С/С++-функции соответствуют параметрам собственного метода (если собственный метод их не содержит, то реализующая его С/С++-функция имеет только два описанных выше параметра).

JNI функции

JNI определяет 210 прикладных функций. Доступ к ним из С/С++-функции можно получить через интерфейсный указатель JNIENV*, который передается каждой С/С++-функции, представлющей реализацию собственного метода. Все функции разделены на 14 групп:

  • информация о версии JNI;
  • операции с классами;
  • исключения (EXCEPTIONS);
  • обработка глобальных и локальных ссылок;
  • операции с объектами;
  • доступ к данным объекта;
  • вызов методов объекта (INSTANCE METHOD);
  • доступ к статическим данным объекта;
  • вызов методов класса (CLASS METHOD);
  • операции со строковыми объектами;
  • операции с массивами;
  • регистрация собственных методов;
  • операции с мониторами (MONITOR OPERATIONS);
  • интерфейс с JVM.

Использование JNI функций необходимо только в том случае, если С/С++-функция осуществляет какое-либо взаимодействие с JVM: вызов JAVA-методов, доступ к данным, создание JAVA-объектов и т.д.


Ниже приведен пример JAVA-программы, которая выводит на печать количество свободной памяти на диске С для платформы WIN32. Для этого используется собственный метод и соответствующая реализационная С/С++-функция, вызывающая при своей работе функцию WIN32 API.

// Файл APP.JAVA
PUBLIC CLASS APP <
PUBLIC STATIC VOID MAIN(STRING ARGS[]) <
SYSTEMSPECIFIC SS = NEW SYSTEMSPECIFIC();
TRY <
LONG BYTES = SS.GETCDRIVEFREESPACE();
IF (BYTES != -1) <
LONG KB = BYTES / 1024;
SYSTEM.OUT.PRINTLN(«на диске C:\\ свободно » + KB + » KB»);
>
ELSE <
SYSTEM.OUT.PRINTLN(«произошла ошибка в С/С++-функции»);
>
>
CATCH (UNSATISFIEDLINKERROR E) <
SYSTEM.OUT.PRINTLN(«метод не найден (» + E + «)»);
>
>
>
CLASS SYSTEMSPECIFIC <
STATIC <
TRY <
SYSTEM.LOADLIBRARY(«SYSSPEC»);
>
CATCH (UNSATISFIEDLINKERROR E) <
SYSTEM.OUT.PRINTLN(«библиотека не найдена (» + E + «)»);
>
>
NATIVE LONG GETCDRIVEFREESPACE();
>
// Файл SYSTEMSPECIFIC.CPP
#INCLUDE «SYSTEMSPECIFIC.H»
#INCLUDE <WINDOWS.H>
JNIEXPORT JLONG JNICALL JAVA_SYSTEMSPECIFIC_GETCDRIVEFREESPACE (JNIENV *, JOBJECT) <
DWORD SCTRPERCLSTR, BYTESPERSCTR, FREECLSTR, CLSTR;
BOOL RES = GETDISKFREESPACE(«C:\\», &SCTRPERCLSTR, &BYTESPERSCTR, &FREECLSTR, &CLSTR);
RETURN RES == TRUE ? SCTRPERCLSTR * BYTESPERSCTR * FREECLSTR : -1;
>

Для успешной компиляции кода JAVA и С/С++ на платформе WIN32 необходимо правильно установить переменные окружения PATH, LIB, INCLUDE и CLASSPATH (многие из этих значений можно задать как параметры соответствующих компиляторов).

Если записать исходные тексты предыдущего примера в файлы APP.JAVA и SYSTEMSPECIFIC.CPP соответственно, то для их компиляции необходимо выполнить следующие команды (предполагается, что исходные файлы находятся в каталоге C:\TEST\NATIVE):

C:\TEST\NATIVE> JAVAC APP.JAVA
C:\TEST\NATIVE> JAVAH -JNI SYSTEMSPECIFIC
C:\TEST\NATIVE> CL -W3 SYSTEMSPECIFIC.CPP -FESYSSPEC.DLL -TP -LD -MD -LINK JAVAI.LIB

Для запуска программы необходимо выполнить: C:\TEST\NATIVE> JAVA APP
на диске С:\ свободно 324567 KB
C:\TEST\NATIVE>

Для трансляции С/С++-файлов можно использовать любой компилятор, допускающий создание 32-битных DLL.

Использование INVOCATION API

Использование INVOCATION API позволяет встраивать JVM в приложения без необходимости их статического связывания с кодом самой JVM. Напомним, что в этом случае управление изначально находится в С/С++-программе. INVOCATION API состоит из небольшого набора функций, позволяющих создавать и уничтожать JVM в текущем процессе, присоединять и отсоединять текущий поток от JVM (интерфейсный указатель существует только в рамках данного потока).

В общем случае, для встраивания JVM в программу ее необходимо создать и проинициализировать, присоединить, если это необходимо, к какому-либо потоку, а по окончании работы с JVM удалить ее из памяти процесса. После того как JVM создана и получен интерфейсный указатель, можно использовать любые JNI-функции.

Рассмотрим пример С++-кода, который создает JVM в процессе своей работы и вызывает статический метод JAVA-класса. Ниже приведены исходные тексты JAVA-класса и C++-кода:

// Файл INVOCATIONAP.JAVAI
PUBLIC CLASS INVOCATIONAPI <
STATIC VOID TEST() <
SYSTEM.OUT.PRINTLN(«HELLO FROM JAVA CODE»);
>
>
// Файл INVOCATIONAPI.CPP
#INCLUDE <JNI.H>
VOID MAIN() <
JAVAVM* JVM;
JNIENV* ENV;
// инициализация
JDK1_1INITARGS VMARGS;
JNI_GETDEFAULTJAVAVMINITARGS(&VMARGS);
VMARGS. ;
// создание JVM
JNI_CREATEJAVAVM(&JVM, &ENV, &VMARGS);
// получение ссылки на класс INVOCATIONAPI
J );
// вызов статического метода TEST
JMETHOD );
ENV->CALLSTATICVOIDMETHOD(CLS, MID);
// удаление JVM
JVM->DESTROYJAVAVM();

Для компиляции приведенной программы на платформе WIN32 необходимо выполнить следующие команды (предполагается, что переменные окружения PATH, LIB, INCLUDE и CLASSPATH установлены верно и исходные файлы находятся в каталоге C:\TEST\NATIVE):

C:\TEST\NATIVE\ JAVAC INVOCATIONAPI.JAVA
C:\TEST\NATIVE\ CL -W3 -NOLOGO INVOCATIONAPI.CPP -FEINVOCATIONAPI -TP -MD -LINK
-NOLOGO JAVAI.LIB

А для запуска программы:

C:\TEST\NATIVE\ INVOCATIONAPI
HELLO FROM JAVA CODE
C:\TEST\NATIVE\

На первый взгляд, наличие JNI и полная публикация его спецификаций может привлечь разработчиков к написанию непереносимого JAVA-кода, что, в свою очередь, может значительно снизить эффективность применения технологии JAVA. Однако на самом деле JNI способствует обратному процессу.

Практически для любого приложения можно совершенно точно определить необходимость в многоплатформенном исполнении. Для систем, которые не нуждаются в многоплатформенности, JNI предоставляет инфраструктуру, с помощью которой JAVA-приложение может взаимодействовать с операционной системой и аппаратурой, в среде которых оно исполняется. Таким образом, JNI является естественным дополнением JAVA-технологии. Он позволяет использовать ее как для создания переносимых (клиентских) приложений, так и для создания высокопроизводительных (серверных) систем, использующих всю специфику конкретной платформы и аппаратуры, сохраняя в то же время главное достоинство JAVA — современный объектно-ориентированный подход.

1c взаимодействие с java

На предприятии есть 1с 8.2 конфигурация управление торговлей. Хочу написать на android клиент для менеджеров, чтобы они могли со своих телефонов сбрасывать заявки сразу в 1с.

Подскажите с помощью чего это лучше провернуть, готовое решение от 1с не предлагать, ибо хочется своими руками и без участи программистов 1с . Первое что мне пришло в голову, напрямую лепить данные в базу 1с, которая развёрнута с помощью Microsoft SQL Server, но я не уверен что 1с сможет эти данные подхватывать.

Может есть готовое API или ещё что то?

6 ответов 6

Вот чисто беспроблемный варианты — это сделать почтовый ящик, и отправлять заявки сгенерированные в клиенте на него, а чтобы 1С их забирала. Или можно через dropbox попробовать. А в SQL писать можно конечно, но как уже сказали структура специфическая + встает вопрос а как ты из внешки будешь цепляться ? Вряд ли ИТшники согласятся Скуль на котором база предприятия светить во внешку.

Формат на ваше усмотрение. Делаете почтовый ящик какой-то отдельный для данных задач, из Androida легко отправить на почту. В 1с, нужно сделать регламентное задание (фоновое), которое будет читать данные и формировать документ Заявок (примеров чтения из почтового ящика на infostart.ru дофига). Все равно на стороне 1С придется делать фоновое задание, которое преобразует входящие данные в удобоваримый 1Совский вид. Ну просто нет шансов что вы на уровне таблиц sql простроите все связи между элементами данных так же, как это требуется для работы самой 1С, это лучше делать уже через встроенные 1Совские механизмы.

Цукерберг рекомендует:  Новичкам в UNIX 10 советов, экономящих время

Привет, да твой вариант вполне рабочий, единственный момент что у 1с своеобразная структура базы, но все волне решаемо. Есть еще вариант сделать WebApi сервер, например на битрексе, который будет принимать данные от пользователей и выгружать уже их в 1с. Правда в данном случае появится задержка между отправкой и появлении в 1с

1с:предприятие — Вызов метода Java из кода C++

Профиль
Группа: Участник
Сообщений: 156
Регистрация: 11.12.2008

Репутация: нет
Всего: нет


Нужно вызвать метод у java- объекта через JNI.
Вопрос 1: Как получить указатель на интерфейс env?
Вопрос 2: Как получить уже созданный в памяти java-объект (именно получить с уже заполненными полями, а не создать новый экземпляр)?

Это сообщение отредактировал(а) x8m6 — 17.12.2010, 00:02

Leprechaun Software Developer

Профиль
Группа: Модератор
Сообщений: 15658
Регистрация: 24.3.2004

Репутация: 206
Всего: 533

LSD
Дата 17.12.2010, 17:24 (ссылка) | (нет голосов) Загрузка .
Цитата(x8m6 @ 16.12.2010, 23:36 )
Как получить указатель на интерфейс env?
Код
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
(JNIEnv * env, jobject jobj, jstring name) <
.
Цитата(x8m6 @ 16.12.2010, 23:36 )
Вопрос 2: Как получить уже созданный в памяти java-объект (именно получить с уже заполненными полями, а не создать новый экземпляр)?

Профиль
Группа: Участник
Сообщений: 156
Регистрация: 11.12.2008

Репутация: нет
Всего: нет

x8m6
Дата 17.12.2010, 23:14 (ссылка) | (нет голосов) Загрузка .
Цитата
Это будет первый аргумент переданный в этот метод.

Мне нельзя вызывать нативный код в Java.

Цитата
Вызвать Java метод который вернет тебе ссылку на этот объект
Код
jobject thd = . ; /* a java.lang.Thread instance */
jmethodID mid;
j > (*env)->FindClass(env, «java/lang/Runnable»);
if (runnableIntf == NULL) <
. /* error handling */
>
m >GetMethodID(env, runnableIntf, «run», «()V»);
if (m > . /* error handling */
>
(*env)->CallVoidMethod(env, thd, mid);
. /* check for possible exceptions */

т.е. получается чтобы вызвать метод у объекта уже нужно иметь ссылку на этот объект — в данном примере thd. Откуда ее взять? Или можно ее занулить, тогда после вызова метода вернется объект?
И как получить нужный мне экземпляр(c требуемыми значениями полей)? Я так понимаю что никак. Единст. способ — это синглтон.

Это сообщение отредактировал(а) x8m6 — 17.12.2010, 23:18

Leprechaun Software Developer

Профиль
Группа: Модератор
Сообщений: 15658
Регистрация: 24.3.2004

Репутация: 206
Всего: 533

LSD
Дата 20.12.2010, 12:09 (ссылка) | (нет голосов) Загрузка .
Цитата(x8m6 @ 18.12.2010, 00:14 )
Мне нельзя вызывать нативный код в Java.

Тогда я вообще не понимаю, как твой код на C++ должен получить управление.

Цитата(x8m6 @ 18.12.2010, 00:14 )
т.е. получается чтобы вызвать метод у объекта уже нужно иметь ссылку на этот объект — в данном примере thd. Откуда ее взять? Или можно ее занулить, тогда после вызова метода вернется объект?
И как получить нужный мне экземпляр(c требуемыми значениями полей)? Я так понимаю что никак. Единст. способ — это синглтон.

Профиль
Группа: Участник
Сообщений: 156
Регистрация: 11.12.2008

Репутация: нет
Всего: нет

x8m6
Дата 20.12.2010, 20:47 (ссылка) | (нет голосов) Загрузка .
Цитата
Тогда я вообще не понимаю, как твой код на C++ должен получить управление.


получить ссылку на запущенную JVM, от туда достать как-то env.

Leprechaun Software Developer

Профиль
Группа: Модератор
Сообщений: 15658
Регистрация: 24.3.2004

Репутация: 206
Всего: 533

LSD
Дата 22.12.2010, 11:44 (ссылка) | (нет голосов) Загрузка .
Цитата(x8m6 @ 20.12.2010, 21:47 )
получить ссылку на запущенную JVM, от туда достать как-то env.

Ты хочешь внедриться в уже запущенную JVM? Ну тогда тебе точно не сюда, с помощью JNI внедрится в другой процесс нельзя. И как это сделать будет зависеть от того какая ОС используется.

А вообще, есть JMX с помощью которого можно из другого процесса получить некие данные из уже запущенной JVM.

Профиль
Группа: Участник
Сообщений: 220
Регистрация: 6.11.2006
Где: Volgograd

Репутация: 2
Всего: 2

Vitaly333
Дата 22.12.2010, 14:00 (ссылка) | (нет голосов) Загрузка .
Цитата
с помощью JNI внедрится в другой процесс нельзя.
Код
_JNI_IMPORT_OR_EXPORT_ jint JNICALL
JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *);

Leprechaun Software Developer

Профиль
Группа: Модератор
Сообщений: 15658
Регистрация: 24.3.2004

Репутация: 206
Всего: 533

LSD
Дата 23.12.2010, 13:28 (ссылка) | (нет голосов) Загрузка .
Цитата(Vitaly333 @ 22.12.2010, 15:00 )
А как же вот этот метод из jni.h:
Цитата
JNI_GetCreatedJavaVMs

jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen,
jsize *nVMs);

Returns all Java VMs that have been created. Pointers to VMs are written in the buffer vmBuf in the order they are created. At most bufLen number of entries will be written. The total number of created VMs is returned in *nVMs.

JDK 1.1.2 does not support creating more than one VM in a single process.

Google
Дата 13.11.2020, 21:35 (ссылка)
Правила форума «Java»
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс «транслит«, если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка «Пометить как решённый» находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

Вызов Java методов из C/C++ кода при помощи JNI на Android.

В прошлой статье посвященной JNI на Android я описывал как начать работу с Android NDK, в том числе, как вызывать нативные методы из Java, на этот раз я опишу как вызывать Java-методы из нативного кода.


Для лучшего понимания этой статьи, рекомендую предварительно ознакомиться с основами JNI в Android. Сам процесс вызова Java-метода из нативного кода далее мы будем называть обратным вызовом, так как такие вызовы чаще всего используются для оповещения Java-стороны приложения о некоторых событиях, просходящих «в нативе». Код доступен на нашем репозитории на GitHub. Для нетерпеливых есть быстрое решение

Общее описание механизма

И так, перед нами стала задача «дернуть» Java-метод из нативного кода. Каков будет порядок действий?

  1. Опеределить какой метод и у какого класса вы хотите вызвать. Банально, да.
  2. Получить дескриптор нужного класса.
  3. Описать сигнатуру метода на языке JNI-примитивов (не так страшно, как звучит).
  4. Получить идентификатор метода (что-то типа ссылки на метод).
  5. Вызвать метод у нужного объекта (если метод экземплярный) или класса (если метод статический).

Определяемя с методами

Я предлагаю всегда делать методами обратного вызова методы интерфейса, а не конкретного класса. У такого подхода есть преимуществ, начиная с того, что можно будет без труда подменять Java-реалзиацию и определять ряд Java-объектов для обработки которых в нативном коде будет достаточно всего одного решения на нативном уровне.

Пусть у нас есть следующий интерфейс NativeCallListener :

Данному интерфейсу в нативном коде на C++ будет соответстововать следующий класс JniNativeCallListener :

Заранее хочу обратить внимание, что действия по инциализации объекта будут местами избыточны для случая однопоточного приложения, но обязательны при использовании многопоточности и pthread.

Теперь перейдем к непосредственной инициализации и получению дескрипторов.

Получаем дескриптор класса и методов

К получению дескриптора класса есть два подхода. Первый мне нравится:

Второй не очень:

Преимущества первого подхода в отсутсвии хард-кода, второй подход позволяет получить дескриптор (jclass) не имея ссылки на объект. Так что решение о том, какой из них использоват зависит от ситуации.

Полностью инициализация будет выглядеть так:

В данном случая, лаконичности ради, я провожу инициализацию в конструкторе, что является плохим тоном. Гораздо лучшим вариантом был бы отдельный метод инициализации, потому что мы помним, что логика и понетциальные ошибки в конструкторе — это плохо, а при вызовах GetMethodID и GetObjectClass и им подобных могут возникать ошибки (они могут вернуть NULL , хотя со мной такого не случалось).

Логирование определено среди утилит в файле Util.h

Вы можете спросить, а зачем нужны эти строки:

А я напомню, что ссылки на объекты, переданные в JNI-метод, действительны только в пределах времени выполнения этого метода. То есть при попытки обратится к mObjectRef или pJniEnv после выполнения метода мы потерпим фиаско. Поэтому мы создаем глобальную ссылку на mObjectRef , чтобы потому вызвать у него нужный метод, и получаем ссылку на Java-машину mJVM , что при помощи нее в будещем получать JNIEnv . Вот так все запутанно, но походу дела все станет понятнее Чтобы получать JNIEnv используется метод getJniEnv():

Определение сигнатуры и получение идентификатора метода

Этим целом служат строки которые вы уже видели:

В качестве параметров GetMethodID служат дескриптор класса, имя метода и дикое, на первый взгляд, описание списка параметров и возвращаемого типа «()V» и «(Ljava/lang/String;)V». В скобках указаны входные параметры, после них — возвращаемый тип. Таким образом первый метод не имеет входных параметров и ничего не возвращает, а второй принимает строку и тоже ничего не возвращает. Ниже приведена таблица типов параметров и пару примеров описания методов.

Поддерживаемые JNI типы данных и их коды

Java JNI JNI array Код Код массива
boolean jboolean jbooleanArray Z [Z
byte jbyte jbyteArray B [B
char jchar jcharArray C [C
double jdouble jdoubleArray D [D
float jfloat jfloatArray F [F
int jint jintArray I [I
long jlong jlongArray J [J
short jshort jshortArray S [S
Object jobject jobjectArray L [L
Class jclass нет L [L
String jstring нет L [L
void void нет V нет

Вызов метода обратного вызова

Дескрипторы получены, теперь можно написать код который используя их и JNIEnv произведет нужные действия:

Вот и все! Теперь можно написать клиентский код, который будет использовать уже написанное решение, выглядеть он может так:

На Java-стороне код будет таким:

Все вышесказанное, но очень быстро


Весь код можно сократить до жалких трех строк логики:

Если заморачиваться не нужно — то такой подход сработает, для больших систем я бы все-таки рекомендовал реализовать все на должном уровне Так же не очень разумно делать «циклические вызовы», когда Java-код вызывает С++ код, который вызывает Java-код, который снова вызывает C++ код. Четвертый шаг тут явно избыточный.

Заключение

JNI — замечательный, но требующий бережного отношения к себе механизм, дающий понимание работы многих аспектов Java и программирования в целом. В следующий раз мы с вами займемся доступом к полям объекта. Код для данной статьи тут.

Jni: вызов java-метода в коде C++, ошибки

Я работаю над проектом для моей инженерной школы, мне нужно подключить два устройства Android через Wi-Fi. Один из устройств Android включит свою точку доступа, а другое устройство подключится к ней.

Я работаю с Qt 5.4.1 (Qt Creator 3.4.1) в C++.

При поиске в Интернете я нашел этот Java-класс: WifiManager.java, который имеет методы для (dis/en) доступной точки доступа Wifi: setWifiApEnabled

Следуя советам Богдана Ватры, я успешно вызвал Java-метод в коде C++. Например, для функции фибоначчи:

Но если я хочу сделать то же самое для функции «setWifiApEnabled», из «WifiApManager.java»:

Он хорошо компилируется, но когда я это понимаю, он говорит, что «myJavaWifiApManager недействителен». Поэтому, если я пытаюсь вызвать setWifiApEnabled или getWifiApConfiguration он getWifiApConfiguration . Любая идея, почему объект недействителен? Он описан в «WifiApManager.java», расположенном в «%% PROJECT_FOLDER %%/android/src/org/app/test»

Если я объявляю объект как myJavaWifiApManager = QAndroidJniObject(«org/app/test/JniMath»); , объект действителен

JNI взаимодействие Java с C/C++

Язык программирования Java, несмотря на имеющие место недостатки, является мощным и, главное, в большинстве случаев самодостаточным языком программирования. Под самодостаточностью понимается возможность написания программ, решающих какую-либо конкретную задачу без привлечения других языков программирования.

Однако иногда без использования вспомогательных средств создать полноценную программу согласно техническим требования невозможно. Например, необходимо воспользоваться кодом, который обеспечивает низкоуровневый доступ к «железу» компьютера, на котором выполняется программа. В языке программирования Java, который по своей идеологии является многоплатформенным, средства низкоуровневого доступа к аппаратной части просто-напросто не предусмотрены. С другой стороны, к моменту появления языка Java в мире программирования уже существовали колоссальные «залежи» программ и библиотек, позволяющих решать практически любые задачи, начиная от математических вычислений и заканчивая управлением сложными системами. Естественно, не замечать это богатство было бы просто неразумно.

Разработчики Java включили в язык возможность обращения из приложений к программам, реализованным на других языках программирования с использованием так называемых native-методов. Подсистема Java, реализующая данную возможность, называется JNI (Java Native Interface – интерфейс обращения к native-методам). К native методам следует прибегать в том случае, когда необходимо воспользоваться сторонней dll библиотекой, чтобы ускорить критичный алгоритм за счет оптимизированного кода на C/С++ или ассемблере. Например, для обработки потокового медиа, сжатия, шифрования и т.п.

Зачастую native-методы просты: они не вызывают исключений, не создают новые объекты в heap’e, не обходят stack, не работают с handle’ами и не синхронизованы. Можно ли для них не делать лишних действий? В статье будет затронуты вопросы о недокументированных возможностях HotSpot JVM для ускоренного вызова простых JNI методов.

Процедуры, обеспечивающие связь native-метода с программой Java, зависят от применяемой операционной системы и языка программирования, на котором native-методы реализованы. В статье рассматривается связь языка Java с библиотеками DLL операционных систем семейства Microsoft Windows. Рассмотрим простой вариант использования JNI. Пример показывает насколько несложно использовать данный механизм.

Пример использования JNI в java приложениях

Допустим, необходимо создать dll библиотеку, методы которой получают и обрабатывают текстовые и целочисленные значения. Результат обработки возвращается также в текстовом и целочисленном виде. Для решения данной задачи создаем проект jnicall-desk, в который включаем обычный класс JNICall.java с описанием методов. Реализация методов будет выполнена в библиотеке dll. На следующем скриншоте приведена структура проекта. Класс JNICallTest.java будет использоваться для тестирования методов библиотеки dll.

Листинг класса JNICall.java

Класс JNICall.java включает три метода, при описании которых указываем модификаторы native. Для загрузки библиотеки dll (JNICall.dll) используется системный вызов loadLibrary(). Необходимо отметить, что наименование загружаемой библиотеки указывается без расширения «.dll».

Класс располагается в пакете com.example, и на это следует обратить пристальное внимание. Это очень важный момент, поскольку наименование пакета будет использовано в наименованиях методов в коде С/С++. Далее перемещать данный класс в другой пакет не следует; иначе не будет загружаться динамическая библиотека.

В проекте классы .java компилируются в директорию bin, где «раскладываются» по пакетам (поддиректориям). После компиляции класса JNICall получаем в проекте файл com.example.JNICall.class. Для создания файла с заголовком для C/C++ обработаем класс утилитой javah, которая создаст файл «com_example_JNICall.h».

Параметр classpath ссылается на директорию bin (-classpath bin). Для класса JNICall приводится полное имя : [package].[Class.name].

Листинг com_example_JNICall.h

В результате выполнения команды javah в корневой директории проекта (где была выполнена команда) будет создан файл com_example_JNICall.h следующего вида:

Следует обратить внимание на 2-ю строчку, в которой выполняется импорт файла . Месторасположение данного файла следует искать в JDK в директории include. На моем компьютере он располагается в директории
«C:\Program Files\Java\jdk1.8.0_131\include». Файл «jni.h» потребовал дополнительно «jni_md.h» из поддиректории win32
(«C:\Program Files\Java\jdk1.8.0_131\include\win32»).


Проект создания dll

Для создания библиотеки JNICall.dll был использован CodeBlocks, который «легко» устанавливается на компьютер. При создании проекта выбран тип «Dynamic Link Library», в который включен файл com_example_JNICall.h и добавлен файл com_example_JNICall.cpp. На следующем скриншоте представлен проект создания библиотеки из двух файлов.

Редактировать заголовок не следует; его необходимо включить в файл CPP (com_example_JNICall.cpp), который представлен ниже :

В коде класса com_example_JNICall.cpp следует обратить внимание на обработку строк. Целочисленные переменные обычно не так сложны. Текстовые же значения нужно «вытащить» из Unicode в обычный char.

Метод doMultiply возвращает значение произведения двух чисел. Метод doCombine — создает одну строку из двух строк. Метод getMessage к текстовой строке «Get text : » добавляет строку, полученную в качестве параметра.

Примечание : в Windows создаваемая библиотека будет называться JNICall.dll; в Linux она должна иметь наименование libJNICall.so, т.е. обязательно начинаться с префикса ‘lib’. Несмотря на то, что в Linux наименование библиотеки .so имеет префикс, при загрузке в LoadLibrary префикс не используется.

Класс тестирования JNI

Обратите внимание, что в проекте jnicall-desk созданная библиотека располагается в поддиректории «dll». Чтобы JVM могла найти эту библиотеку следует внести исправления в строку загрузки класса JNICall.java :

Это работает для standalone-приложения, но это НЕПРАВИЛЬНО. А если динамическую библиотеку необходимо будет использовать в WEB-приложении, то данный подход уже не сработает, библиотека не будет найдена и Вы увидите исключение java.lang.UnsatisfiedLinkError. ПРАВИЛЬНЫЙ подход — это использование «java.library.path», который работает как в standalone приложении, так и WEB-приложении. Пример использования JNI в сервлете WEB-приложения, включающего ajax-вызов библиотеки JQuery, можно увидеть здесь.

Коротко о java.library.path

«java.library.path» это, если можно так выразиться, аналог >

На просторах Интернета можно найти 2 примера как изменить данное свойство в run-time. Вы можете посмотреть первоисточник откуда получен следующий метод setLibraryPath :

Данный метод был протестирован, он рабочий, но мне, откровенно говоря, не очень понравился, поскольку обнуляет поля. В примере JNICallTest.java для тестирования JNI был использован второй метод addLibraryPath.

Листинг класса JNICallTest.test

В классе выполняется проверка наличия библиотеки dll в поддиректории. Если библиотека найдена, то добавляется путь к dll в переменную «usr_paths» загрузчика класса ClssLoader. Остальное все тривиально и прозрачно, особых комментариев не требуется. Помните, что в системный метод загрузки библиотеки loadLibrary в классе JNICall необходимо передать только ее наименование, т.е. использовать код : System.loadLibrary(«JNICall»).

В классе выполняется проверка наличия библиотеки в поддиректории, после чего определяется путь к директории и вызывается метод addLibraryPath, который добавляет этот путь в соответствующее свойство загрузчика. Далее вызываются native методы.

Результат выполнения программы выводится в консоль :

Исходный код примера можно скачать в конце страницы.

Особенности использования JNI

Но зачастую native методы просты: они не вызывают исключений, не создают новые объекты в heap’e, не обходят стек, не работают с хендлами и не синхронизованы. Можно ли для них не делать лишних действий?

Далее в статье будут рассмотрены недокументированные возможности HotSpot JVM для ускоренного вызова простых JNI методов.
Рассмотрим для примера простой native метод, получающий на вход массив byte[] и возвращающий сумму элементов. Есть несколько способов работы с массивом в JNI:

1. GetByteArrayRegion – копирует элементы Java массива в указанное место нативной памяти.

2. GetByteArrayElements – то же самое, только JVM сама выделяет область памяти, куда будут скопированы элементы. По окончании работы с массивом необходимо вызвать ReleaseByteArrayElements.

Здесь можно задаться вопросом:»Зачем делать копию массива?». Но ведь работать с объектами в Java Heap напрямую из натива нельзя, так как они могут перемещаться сборщиком мусора прямо во время работы JNI метода. Однако есть функция GetPrimitiveArrayCritical, которая возвращает прямой адрес массива в heap’e, но при этом запрещает работу GC до вызова ReleasePrimitiveArrayCritical.

Метод Critical Native

А вот и секретный инструмент. Внешне он похож на обычный JNI метод, только с приставкой JavaCritical_ вместо Java_. Среди аргументов отсутствуют JNIEnv* и jclass, а вместо jbyteArray передаются два аргумента: jint length – длина массива и jbyte* data – «сырой» указатель на элементы массива. Таким образом, Critical Native методу не нужно вызывать дорогие JNI функции GetArrayLength и GetByteArrayElements – можно сразу работать с массивом. На время выполнения такого метода GC будет отложен.

Как можно увидеть, в реализации не осталось ничего лишнего. Но чтобы метод мог стать Critical Native, он должен удовлетворять строгим ограничениям:

  • метод должен быть static и не synchronized;
  • среди аргументов поддерживаются только примитивные типы и массивы примитивов;
  • Critical Native не может вызывать JNI функции, а, следовательно, аллоцировать Java объекты или вызывать исключения;
  • метод должен завершаться за короткое время (самое главное), поскольку на время выполнения он блокирует GC.


Critical Natives задумывался как приватный API HotSpot’a для JDK, чтобы ускорить вызов криптографических функций, реализованных в native методе.

Важная особенность: JavaCritical_ функции вызываются только из горячего (скомилированного) кода, поэтому помимо JavaCritical_ реализации у метода должна быть еще и «запасная» традиционная JNI реализация. Впрочем, для совместимости с другими JVM так даже лучше.

Давайте, измерим, какова же экономия при работе с массивами разной длины: 16, 256, 4KB, 64KB и 1MB. Естественно, с помощью JMH.

Оказывается, для маленьких массивов стоимость JNI вызова в разы превосходит время работы самого метода! Для массивов в сотни байт накладные расходы сравнимы с полезной работой. Ну, а для многокилобайтных массивов способ вызова не столь важен – всё время тратится собственно на обработку.

Critical Natives – приватное расширение JNI в HotSpot, появившееся с JDK 7. Реализовав JNI-подобную функцию по определенным правилам, можно значительно сократить накладные расходы на вызов native метода и обработку Java-массивов в нативном коде. Однако для долгоиграющих функций такое решение не подойдет, поскольку GC не сможет запуститься, пока исполняется Critical Native.

Источник информации об особенностях использования JNI можно увидеть здесь.

Скачать пример

Рассмотренный на странице пример, включающий проект Eclipse с использованием JNI, можно скачать здесь (37.2 Кб). Библиотека JNICall.dll создана для windows x64.

Пример использования JNI в сервлете WEB-приложения можно увидеть здесь.

Java для программиста 1С

Содержание

Опишем только основные особенности и часто используемые возможности языка Java в сравнении со встроенным языком 1С, покажем аналоги часто используемых объектов: “структура”, “соответствие”, “таблица значений” и т.п.

Цель этого документа – помочь программисту 1С максимально быстро начать разработку на новом для него языке Java.

Часто используемые примитивные типы

У примитивных типов есть замещающие классы (классы-обертки), пишутся с большой буквы. Были придуманы для того, чтобы иметь возможность оперировать с простыми числами как с объектами.

Вы можете использовать их параллельно с примитивными типами, абсолютно прозрачно:

Вызов метода Java — c++

Я создал свой первый родной вызов в Java с Android SDK сегодня.
Я нашел несколько примеров, но они не согласуются с головкой функции.

Я всегда использовал

Принадлежность к головам различна, чтобы получить класс вызывающего.
Но что является предпочтительным способом?

Из кода С++ я хочу вызвать java-метод. Я нашел документацию JNI (Метод вызова методов экземпляра).
Но я не знаю, каким должен быть первый параметр (объект).

Я попытался предоставить экземпляр класса, который я получаю от вызова собственного метода, который не работает с AbstractMethodError.

Исправлен исходный код:

Спасибо за любые советы.

    2 2
  • 1 ноя 2020 2020-11-01 00:40:06
  • CSchulz

2 ответа

jobject может быть любым java-объектом, тогда как jclass должен быть объектом, представляющим java.lang.Class . В C API jobject и jclass — это то же самое, в то время как С++ API пытается обеспечить безопасность типов и объявляет их как разные типы, где jclass наследует jobject . Второй аргумент должен представлять класс, поэтому jclass является предпочтительным. Даже если в C это не имеет значения, это может намекнуть разработчику или даже сэкономить ваше время, если вы когда-нибудь решите переключиться на С++. Подробнее о типах JNI здесь.

  • 1 ноя 2020 2020-11-01 00:40:07
  • [email protected]

Вызов [тип] Метод предназначен только для частных методов и конструкторов. (Метод вызова методов экземпляра) Когда вы вызываете открытый метод, вы получите AbstractMethodError.

Цукерберг рекомендует:  Визуализация - Визуализация методов сортировки(C# Windows Forms)
Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих