- JNI: Подружим Java и C++
- Постановка задачи
- Генерация заголовков
- Реализация на C++
- Загрузка в Java
- Передача параметров
- Заключение
- How to compile dynamic library for a JNI application on linux?
- Русские Блоги
- Пример вызова интерфейсной функции библиотеки so программой JAVA под Linux (JNA)
- 1. Введение
- 2. Процесс вызова JNI
- 3. Процесс вызова JNA
- 4. В Linux пример программы JAVA, вызывающей библиотечную функцию so с помощью технологии JNA.
- 4.1 Среда разработки
- 4.2 генерация библиотеки so
- 4.2.1 Запись файла sayhello.h
- 4.2.2 Запись файла sayhello.c
- 4.2.3 Компиляция командных файлов 64cmd.sh и 32cmd.sh
- 4.2.4 Скомпилировать и сгенерировать библиотеку so
- 4.3 Подготовка программы JNA
- 4.3.1 Напишите интерфейсный файл Clibrary.java для создания экземпляра библиотеки so
- 4.3.2 Написание метода вызова функции интерфейса C JNATest.java
- 4.4 Запуск программы JAVA
- 5. Резюме
JNI: Подружим Java и C++
Бывают моменты, когда в Java некоторые действия выполняются за пределами обычных Java-классов. Например, необходимо исполнить код, написанный на C/C++ или другом каком-нибудь языке.
В данной статье рассмотрим данный вопрос с практической точки зрения, а именно напишем простой пример взаимодействия кода Java с кодом C++, используя JNI . Статья не содержит чего-то сверхестественного, это скорее памятка для тех, кто с этим не работал.
Для наших целей существует возможность динамической загрузки нативных библиотек, вызываемая методом System.load() , о чем более подробно можно прочитать здесь.
Постановка задачи
Пусть нам необходимо реализовать класс, содержащий в себе нативный метод, выводящий на экран “Hello world”.
package ru.forwolk.test; public class JNIHelloWorld
Генерация заголовков
Сгенерируем заголовки данного класса для C++.
Сначала создадим папку в корне проекта, где будем собирать бинарники:
Затем, скомпилируем наш класс в данную директорию
javac -d bin/ src/ru/forwolk/test/JNIHelloWorld.java
В папке bin у нас появился class-файл. Вернее, в bin/ru/forwolk/test/. Переидем в папку bin и сгенерируем заголовки.
cd bin/ javah ru.forwolk.test.JNIHelloWorld
Как видно, в нашей папке bin появился файл ru_forwolk_test_JNIHelloWorld.h. Для простоты переименуем его в JNIHelloWorld.h
mv ru_forwolk_test_JNIHelloWorld.h JNIHelloWorld.h
Открыв его, видим следующую картину:
JNIHelloWorld.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class ru_forwolk_test_JNIHelloWorld */ #ifndef _Included_ru_forwolk_test_JNIHelloWorld #define _Included_ru_forwolk_test_JNIHelloWorld #ifdef __cplusplus extern "C" < #endif /* * Class: ru_forwolk_test_JNIHelloWorld * Method: printHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_ru_forwolk_test_JNIHelloWorld_printHelloWorld (JNIEnv *, jobject); #ifdef __cplusplus >#endif #endif
Реализация на C++
Создадим файл с исходниками JNIHelloWorld.cpp. Я для этой цели создал проект в Clion, в который вставил необходимый файл. Реализуем наш метод.
JNIHelloWorld.cpp #include #include "JNIHelloWorld.h" JNIEXPORT void JNICALL Java_ru_forwolk_test_JNIHelloWorld_printHelloWorld (JNIEnv *, jobject)
Чтобы в Clion все работало корректно, необходимо в файл CMakeLists.txt добавить библиотеки Java:
// Вместо $JAVA_HOME -- путь до Java include_directories($JAVA_HOME/include) include_directories($JAVA_HOME/include/linux) link_directories($JAVA_HOME/include) link_directories($JAVA_HOME/include/linux)
g++ -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -fPIC JNIHelloWorld.cpp -shared -o helloworld.so -Wl,-soname -Wl,--no-whole-archive
Загрузка в Java
В корневой папке проекта появился файл helloworld.so. Переместим его в папку bin/ проекта Java.
Теперь необходимо загрузить нашу библиотеку. Хорошей практикой будет статическая загрузка библиотеки прямо в классе. Добавим загрузку прямо в класс JNIHelloWorld
Теперь мы можем полноценно использовать данный класс. Давайте проверим.
public static void main(String[] args)
Hello world! Process finished with exit source 0
Передача параметров
А что делать, если нам надо не только выполнять какой-то код, а также передавать параметры и получать ответ? Рассмотрим еще один метод, выполняющий умножение двух чисел. Добавим в класс JNIHelloWorld метод
native int multiply(int a, int b);
Выполняем те же самые действия, описанные выше по генерации заголовков. Как видим, сгенерировалось следующее
/* * Class: ru_forwolk_test_JNIHelloWorld * Method: multiply * Signature: (II)I */ JNIEXPORT jint JNICALL Java_ru_forwolk_test_JNIHelloWorld_multiply (JNIEnv *, jobject, jint, jint);
Реализуем метод в JNIHelloWorld.cpp
JNIEXPORT jint JNICALL Java_ru_forwolk_test_JNIHelloWorld_multiply (JNIEnv *, jobject, jint a, jint b)
Опять же произведем описанные выше действия по подтягиванию библиотеки, добавим строку в main по выводу результата произведения двух чисел и запустим
public static void main(String[] args)
4 Hello world! Process finished with exit source 0
Заключение
Мы с вами рассмотрели возможность Java использовать код, написанный на C/C++. Это можно применять в различных целях, например, для увеличения скорости исполнения кода, для защиты кода от прямого вмешательства и для прочих целей. Очень надеюсь, что данная статья поможет вам разобраться в основах JNI.
Весь код я выложил в открытый доступ. В директории cpp разместил класс C++ без лишних файлов проекта Clion.
How to compile dynamic library for a JNI application on linux?
And now the part where I think I screwed up. I was inspired by this guide (Compile the Dynamic or Shared Object Library section):
dierre@cox:~/Scrivania/provajni$ gcc -I"/usr/lib/jvm/java-6-sun/include" -I"/usr/lib/jvm/java-6-sun/include/linux" -o hellolib.so -shared -Wl,-soname,hello.so Hello.cpp -static -lc
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hellolib in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734) at java.lang.Runtime.loadLibrary0(Runtime.java:823) at java.lang.System.loadLibrary(System.java:1028) at Hello.(Hello.java:4) Could not find the main class: Hello. Program will exit.
LD_LIBRARY_PATH=`pwd` export LD_LIBRARY_PATH
with no results. I know I’m doing something extremely stupid but I can’t figure out what it is. The dynamic lib is generated with the -shared option, isn’t it? Update #1 I tried static < System.load("/home/dierre/Scrivania/provajni/hellolib.so"); >to see if that worked but now:
Exception in thread "main" java.lang.UnsatisfiedLinkError: /home/dierre/Scrivania/provajni/hello.so: /home/dierre/Scrivania/provajni/hello.so: undefined symbol: _ZSt4cout at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1803) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1699) at java.lang.Runtime.load0(Runtime.java:770) at java.lang.System.load(System.java:1003) at Hello.(Hello.java:4)
Update #2 Ok, to solve the Update #1 problem I had to use g++ insted of gcc , obviously. Still having trouble to use the load method though. I can’t seem to tell it the right path.
Русские Блоги
Пример вызова интерфейсной функции библиотеки so программой JAVA под Linux (JNA)
1. Введение
Недавно я изучил, как называть язык C таким образом, чтобы интерфейс библиотеки функционировал с помощью java-программ под Linux. Я также нашел некоторую информацию в Интернете, а затем просто провел некоторую проверку разработки. В частности, вот резюме. Однако перед объяснением кода , Вам нужно вкратце представить читателям отношения между JNI и JNA, не слишком много места, не много ерунды, начните.
2. Процесс вызова JNI
JNI — это сокращение от Java Native Interface. Он предоставляет несколько API-интерфейсов для реализации связи между Java и другими языками (в основном C и C ++). Процесс вызова JNI примерно следующий:
Из вышеизложенного видно, что использование технологии JNI для вызова библиотеки so немного хлопотно, поскольку необходимо понимать не только программирование на JAVA, но и программирование на языке C.
3. Процесс вызова JNA
JNA (Java Native Access) предоставляет набор классов инструментов Java для динамического доступа к собственным системным библиотекам (собственная библиотека: например, DLL Window) во время выполнения без написания кода Native / JNI. Разработчикам нужно только описать функцию и структуру целевой нативной библиотеки в интерфейсе Java, и JNA автоматически реализует отображение из интерфейса Java в нативную функцию. Лично понимаю, что JNA — это дальнейшая оптимизация для JNI.
Как видно из вышеизложенного, при использовании технологии JNA для вызова библиотеки so, шаги намного упрощены по сравнению с JNI, здесь требуется только пакет JNA jar и понимание программирования JAVA, тогда давайте начнем собственно бой!
4. В Linux пример программы JAVA, вызывающей библиотечную функцию so с помощью технологии JNA.
Я заранее заявил, что, поскольку java-программе нужна библиотека so для вызова, я не нашел подходящей библиотеки so в Интернете, поэтому я написал ее на основе информации в Интернете. Затем я сначала представлю создание библиотеки so, а затем код JNA. На следующем рисунке показана структура кода:
4.1 Среда разработки
Система Linux: Linux localhost.localdomain 2.6.32-642.el6.x86_64 redhat
Версия jdk: версия java «1.8.0_191» 64-бит
Версия пакета JNA jar: jna-4.2.1.jar
4.2 генерация библиотеки so
4.2.1 Запись файла sayhello.h
Создайте каталог c_code и напишите код файла заголовка:
#include #include int sayHello(); int add(int a, int b); char* outputString(char* str);
Заголовочный файл объявляет 3 функции, конкретная реализация относится к sayhello.c в 4.2.2.
4.2.2 Запись файла sayhello.c
В каталоге c_code напишите код для файла .c:
#include "sayhello.h" /** * Распечатать Hello World! */ int sayHello() < printf("Hello World!\n"); return 1; >/** * Возвращает целочисленный тип, результат сложения двух чисел */ int add(int a, int b) < return a + b; >/** * Тип возвращаемой строки, конкретная строка вывода + данные, введенные пользователем */ char* outputString(char* str) < char* strTmp[128] = ; strcpy(strTmp, "your input String is :"); strcat(strTmp, str); return strTmp; >
Файл .c записан с 3 функциями, реализованные функции напрямую выводят «Hello World!», Java вводит два числа и добавляет их в c, чтобы вернуть результат типа int, и выводит результат типа строки (конкретная строка + java Программа передает строку).
4.2.3 Компиляция командных файлов 64cmd.sh и 32cmd.sh
В каталоге c_code запишите файлы команд компиляции 64cmd.sh и 32cmd.sh.
Код 64cmd.sh выглядит следующим образом:
gcc -m64 -fPIC -c ./*.c -I./ gcc -m64 -shared -o libsayhello.so *.o rm *.o cp libsayhello.so ../java_code/
Код 32cmd.sh выглядит следующим образом:
gcc -m32 -fPIC -c ./*.c -I./ gcc -m32 -shared -o libsayhello.so *.o rm *.o cp libsayhello.so ../java_code/
Сравнивая два кода файла 64cmd.sh и 32cmd.sh, нетрудно увидеть, что разница только в gcc -m64 и gcc -m32 после команды gcc. gcc -m64 означает компиляцию 64-битных программ, а gcc -m32 означает компиляцию 32-битных программ (примечание: gcc -m64 используется только для 64-битных машин Linux).
В качестве примера возьмем командный файл 64cmd.sh:
Командная строка 1: скомпилируйте файл .c и выведите файл .o
Командная строка 2: вывод так, библиотека
Командная строка 3: удалить файл .o
Командная строка 4: Скопируйте сгенерированную библиотеку so в каталог java_code для вызова java-программы.
4.2.4 Скомпилировать и сгенерировать библиотеку so
Выполните команду «sh 64cmd.sh», чтобы сгенерировать so library-libsayhello.so (может не сообщать об ошибках прав доступа, тогда вам необходимо выполнить команду «chmod + x 64cmd.sh», чтобы предоставить исполняемые права доступа к файлу).
4.3 Подготовка программы JNA
4.3.1 Напишите интерфейсный файл Clibrary.java для создания экземпляра библиотеки so
import com.sun.jna.Library; import com.sun.jna.Native; public interface Clibrary extends Library
«Clibrary INSTANCE = (Clibrary) Native.loadLibrary (« sayhello », Clibrary.class);» предназначен для создания экземпляра библиотеки so в объект, где параметр sayhello представляет «libsayhello.so», а библиотека будет удалена по умолчанию в JNA. Префикс и суффикс .so.
Следующий код является объявлением трех методов. Обратите внимание, что имя метода должно соответствовать имени функции в коде C.
Прилагается таблица соответствия между типами данных JNA и C:
Native Type | Size | Java Type | Common Windows Types |
char | 8-bit integer | byte | BYTE, TCHAR |
short | 16-bit integer | short | WORD |
wchar_t | 16/32-bit character | char | TCHAR |
int | 32-bit integer | int | DWORD |
int | boolean value | boolean | BOOL |
long | 32/64-bit integer | NativeLong | LONG |
long long | 64-bit integer | long | __int64 |
float | 32-bit FP | float | |
double | 64-bit FP | double | |
char* | C string | String | LPTCSTR |
void* | pointer | Pointer | LPVOID, HANDLE, LPXXX |
4.3.2 Написание метода вызова функции интерфейса C JNATest.java
4.4 Запуск программы JAVA
На этом этапе программа JAVA под Linux вызывает экземпляр интерфейсной функции библиотеки so для завершения и успешного выполнения.
5. Резюме
В этой статье сначала объясняется процесс вызова JNI и JNA, а затем идет написание и выполнение программы JAVA, вызывающей экземпляр интерфейсной функции библиотеки so под Linux, которая реализует вызов 3 различных функций возвращаемого значения. Во всей статье не так много текста, но я чувствую, что он длинноват. В конце концов, код и анализ были добавлены. Надеюсь, это будет полезно читателям. Я также храню копию для справки. Наконец, спасибо читателям за терпение при чтении. Если есть какие-то предложения, которые не являются гладкими или неточными, пожалуйста, поправьте меня! Спасибо! (^__^)
Исходный код программы:https://download.csdn.net/download/sinat_31511255/11006576, (При загрузке нет определения точек, я не знаю, как его изменить, по умолчанию 5 баллов -_- ||, если кто-нибудь знает, как изменить, дайте мне знать, спасибо!)
Наконец, спасибо, читатели, за терпение при чтении. Если есть предложение, которое не является гладким или неточным, вы также можете внести предложения по улучшению! Спасибо! (^__^)