Android java native method

# Android Java Native Interface (JNI)

(opens new window) (Java Native Interface) is a powerful tool that enables Android developers to utilize the NDK and use C++ native code in their applications. This topic describes the usage of Java C++ interface.

# How to call functions in a native library via the JNI interface

(opens new window) (JNI) allows you to call native functions from Java code, and vice versa. This example shows how to load and call a native function via JNI, it does not go into accessing Java methods and fields from native code using JNI functions

Suppose you have a native library named libjniexample.so in the project/libs/ folder, and you want to call a function from the JNITest Java class inside the com.example.jniexample package.

In the JNITest class, declare the function like this:

public native int testJNIfunction(int a, int b); 

In your native code, define the function like this:

#include jni.h> JNIEXPORT jint JNICALL Java_com_example_jniexample_JNITest_testJNIfunction(JNIEnv *pEnv, jobject thiz, jint a, jint b)  return a + b; > 

The pEnv argument is a pointer to the JNI environment that you can pass to JNI functions

(opens new window) to access methods and fields of Java objects and classes. The thiz pointer is a jobject reference to the Java object that the native method was called on (or the class if it is a static method).

In your Java code, in JNITest , load the library like this:

static System.loadLibrary("jniexample"); > 

Note the lib at the start, and the .so at the end of the filename are omitted.

Call the native function from Java like this:

JNITest test = new JNITest(); int c = test.testJNIfunction(3, 4); 

# How to call a Java method from native code

The Java Native Interface (JNI) allows you to call Java functions from native code. Here is a simple example of how to do it:

package com.example.jniexample; public class JNITest  public static int getAnswer(bool)  return 42; > > 
int getTheAnswer()  // Get JNI environment JNIEnv *env = JniGetEnv(); // Find the Java class - provide package ('.' replaced to '/') and class name jclass jniTestClass = env->FindClass("com/example/jniexample/JNITest"); // Find the Java method - provide parameters inside () and return value (see table below for an explanation of how to encode them) jmethodID getAnswerMethod = env->GetStaticMethodID(jniTestClass, "getAnswer", "(Z)I;"); // Calling the method return (int)env->CallStaticObjectMethod(jniTestClass, getAnswerMethod, (jboolean)true); > 

JNI method signature to Java type:

JNI Signature Java Type
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L fully-qualified-class ; fully-qualified-class
[ type type[]

So for our example we used (Z)I — which means the function gets a boolean and returns an int.

# Utility method in JNI layer

This method will help to get the Java string from C++ string.

jstring getJavaStringFromCPPString(JNIEnv *global_env, const char* cstring)  jstring nullString = global_env->NewStringUTF(NULL); if (!cstring)  return nullString; > jclass strClass = global_env->FindClass("java/lang/String"); jmethodID ctorID = global_env->GetMethodID(strClass, "", "([BLjava/lang/String;)V"); jstring encoding = global_env->NewStringUTF("UTF-8"); jbyteArray bytes = global_env->NewByteArray(strlen(cstring)); global_env->SetByteArrayRegion(bytes, 0, strlen(cstring), (jbyte*) cstring); jstring str = (jstring) global_env->NewObject(strClass, ctorID, bytes, encoding); global_env->DeleteLocalRef(strClass); global_env->DeleteLocalRef(encoding); global_env->DeleteLocalRef(bytes); return str; > 

This method will help you to convert jbyteArray to char

char* as_unsigned_char_array(JNIEnv *env, jbyteArray array)  jsize length = env->GetArrayLength(array); jbyte* buffer = new jbyte[length + 1]; env->GetByteArrayRegion(array, 0, length, buffer); buffer[length] = '\0'; return (char*) buffer; > 

Источник

JNI Часть 1: Введение

Всем привет! Меня зовут Роман Аймалетдинов и я разрабатываю клиентское приложение Ситимобил. Решил написать небольшую серию из трех статей по JNI, так как технология используется редко, но иногда она бывает очень полезной. Несмотря на то, что я разрабатываю классические приложения под Android, иногда хочется посмотреть технологии рядом со своей специализацией.

Что такое JNI?

JNI — это интерфейс, позволяющий из Java вызывать нативные функции. Например, метод С++, который что-нибудь делает. Допустим, мы пишем большую программу на простом и любимом Java или Kotlin, и нужно реализовать задачу коммивояжера для нашего клиента. Или мы пишем генетический алгоритм, который ищет что-то в большом объёме данных, и так уж вышло, что у нас есть замечательная реализация на С++. Особенно часто я слышу про JNI в gamedev- и в automotive-проектах. Однажды я работал в таком, будучи ещё интерном, и, точно не помню, но в общих чертах на С++ было много низкоуровневого кода по обработке потока данных, получаемого со спутника. JNI позволяет вручную управлять памятью. Можно написать фрагмент кода на C/С++, и при необходимости дёргать нативный метод и получать из него результат вычисления.

И ещё по поводу производительности: мы все знаем, что С/С++ побыстрее будут, и тогда получается, что мы сможем оптимизировать наши алгоритмы на Java, переписав их на C++. (спойлер: не надо, скорее всего, будет работать медленнее). Некоторые авторы в интернете говорят про увеличение производительности, поэтому в своей третьей статье я приведу тесты JNI/NDK и мы сможем сверить производительность.

Также для Android есть инструмент NDK, который всё про то же — запуск нативного кода и увеличение производительности отдельных алгоритмов.

NDK — Native Development Kit (для Android).
JNI — Java Native Interface (общий интерфейс).

Шаги для запуска кода из JNI

Шаг 1. Создадим новый проект ex: JNI_aymaletdinov_roman.

Шаг 2. Создадим класс, у которого будут native-методы, и назовём его AwesomeLib.

System.loadLibrary(«nativeLib»); — это статический метод, который загружает native-библиотеку из файловой системы в память и делает её экспортированные функции доступными для нашего Java-кода. То есть мы загружаем файл на С/С++, который будет выполнять сложные вычисления. Однако nativeLib мы ещё не создали, сделаем это позже.

Шаг 3. Следующим шагом нам необходимо объявить метод, помеченный ключевым словом native. Он не имеет реализации в Java, напишем её на C++.

public class AwesomeLib < static < System.loadLibrary("nativeLib"); >public native void helloHabr(); //

Шаг 4. Вызовем из Main нашу библиотеку, которая что-то делает в native-коде .

Шаг 5. На Java мы всё сделали! Теперь начинается что-то непривычное: нужно обязательно установить MinGW-w64, иначе не на чем будет компилировать нативный код. При установке выберите x86-64 и пропишите bin переменные среды path.

Шаг 6. Затем необходимо написать реализацию нашего нативного метода на C++. В этом языке объявление и реализация обычно хранятся в файлах .h и .cpp соответственно. Открываем консоль; переходим в папку, в которой будут лежать native-файлы; генерируем заголовок для натива командой javac -h . absolutePath/MyClass.java.

После выполнения команды будет создано два файла: .h и .class .

Декомпилированный .class мало интересен, а вот .h — что-то не из нашего JVM-мира. На самом деле всё очевидно: наш метод сгенерировался таким, каким он будет в С++. При этом имя функции генерируется автоматически с использованием полного имени пакета, класса и метода. Кроме того, мы получаем два параметра, передаваемых нашей функции:

/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class nativelib_AwesomeLib */ #ifndef _Included_nativelib_AwesomeLib #define _Included_nativelib_AwesomeLib #ifdef __cplusplus extern "C" < #endif /* * Class: nativelib_AwesomeLib * Method: sayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_nativelib_AwesomeLib_helloHabr(JNIEnv *, jobject); #ifdef __cplusplus >#endif #endif

Шаг 7. Следующий шаг — создание .cpp . То есть мы сами создаём файл с расширением .cpp и копируем из .h контракт метода. Я назвал файл тем же именем, что и Java-класс: AwesomeLib.cpp .

#include "nativelib_AwesomeLib.h" #include JNIEXPORT void JNICALL Java_nativelib_AwesomeLib_helloHabr(JNIEnv* env, jobject thisObject)

Шаг 8. Затем наш нативный код надо скомпилировать. Когда мы выполним в консоли команду g++ -c -I»C:\Program Files\Java\jdk-12.0.1\include» -I»C:\Program Files\Java\jdk-12.0.1\include\win32″ AwesomeLib.o.cpp -o AwesomeLib.o , появится ещё один файл: .o .

Смотрим на дерево проекта и видим новый файл:

Шаг 9. Осталось совсем немного. Теперь нужно сгенерировать .dll с названием нативной библиотеки, которое мы указали в статике в Java. В консоли выполняем команду: g++ -shared -o nativeLib.dll AwesomeLib.o -Wl,—add-stdcall-alias .

Смотрим, в IDE появился ещё один сгенерированный файл.

Шаг 10. Всё! Хочется запустить нашу программу с нативным кодом, но IDEA сейчас этого сделать не сможет. Она ничего не знает про С++, следовательно, для запуска воспользуемся консолью:
java -cp . -Djava.library.path=»[Абсолютный путь до папки, где лежит dll]» Main.java

Обратите внимание, что я перед запуском выполнил команду cd .. для того, чтобы быть в той же папке, где лежит запускаемый мною файл Main .

Как запустить проект из IDEA, а не через консоль?

Так как IDEA по умолчанию не знает ни о каких .dll , она не сможет запустить наш проект по нажатию на треугольник “Run”. Чтобы запустить проект, нужно прописать VM options в меню “Edit configurations”.

У нас появится поле VM options, и мы добавляем Djava.library.path full-width «>

Вот теперь точно всё! Можно запускать проект из IDEA и радоваться жизни.

Заключение

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

  • Установить MinGW-w64. При установке выбрать x86-64 и прописать bin переменные среды path.
  • Создать NativeLib.java с native-методами.
  • Перейти в консоли в папку (ex: src), в которую хотите сохранить сгенерированные файлы.
  • Выполнить javac -h . AwesomeLib.java .
  • Выполнить g++ -c -I»C:\\Program Files\\Java\\jdk-12.0.1\\include» -I»C:\\Program Files\\Java\\jdk-12.0.1\\include\\win32″ AwesomeLib.cpp -o AwesomeLib.o .
  • Выполнить g++ -shared -o nativeLib.dll AwesomeLib.o -Wl,—add-stdcall-alias .
  • Запустить программу: java -cp . -Djava.library.path=”Путь до папки с .dll» Main (или через студию).

Если вы внесли изменения в .cpp , то заголовок пересоздавать не нужно, но если вы изменили контракт метода, то придётся начинать с этапа javac -h . . .

В следующей части я расскажу про более сложные методы в JNI, про передачу List и вызов Java из C++.

Источник

Читайте также:  Using sessions in java
Оцените статью