существуетВыполняйте разработку JNI без использования IDEВ этой статье мы использовали метод «статической регистрации», чтобы установить однозначное соответствие между собственными методами в мире Java и функциями в мире Native.
Метод «статической регистрации» действительно спасает нас от многих вещей, но у него есть и соответствующие недостатки.
- Когда нативный метод Java впервые вызывается, виртуальная машина ищет функцию соответствующего нативного уровня, которая немного влияет на эффективность выполнения. Если поиск найден, сопоставленные отношения будут установлены, и нет необходимости тратить время поиска в следующий раз.
- Имя функции исходного слоя слишком длинное. Формат имени неправильный.
Java_包名_类名_方法名
,НапримерJava_com_bxll_jnidemo_Hello_helloFromJNI
. - Слишком много хлопот и влияет на эффективность работы (личный опыт работы).
Есть «статическая регистрация» и, конечно, есть «динамическая регистрация», так в чем преимущества и недостатки по сравнению?
- «Динамическая регистрация» требует, чтобы мы вручную установили отношения сопоставления функций, хотя это увеличивает объем кода, но может повысить эффективность работы.
- «Динамическая регистрация» позволяет нам настраивать имена функций.
- По сравнению со "статической регистрацией" эффективность работы выше.
Хотя «динамическая регистрация» имеет так много преимуществ по сравнению со «статической регистрацией», она требует определенных затрат на обучение, но она также очень полезна, так что давайте начнем.
Загрузить динамическую библиотеку
мы знаем, что вJava
слой черезSystem.loadLibrary()
Метод может загрузить динамическую библиотеку, а виртуальная машина вызовет библиотеку JNI.JNI_OnLoad()
Функция (неважно, в какой файл она помещена), прототип функции выглядит следующим образом
#include <jni.h>
jint JNI_OnLoad(JavaVM* vm, void* reserved);
Описание параметра
-
vm
:JavaVM
указатель, мыJavaVM и JNIEnv, соединяющие мир Javaпредставлено в статье. -
reserved
: типvoid *
, этот параметр предназначен для резервирования позиции для будущего использования.
Возвращаемое значение представляет то, что необходимо динамической библиотеке.JNI
версия, конечно, если виртуальная машина не распознает эту возвращенную версию, то динамическая библиотека также не может быть загружена.
В настоящее время существует четыре возвращаемых значения, которыеJNI_VERSION_1_1
, JNI_VERSION_1_2
, JNI_VERSION_1_4
, JNI_VERSION_1_6
.
Если динамическая библиотека не предоставляетсяJNI_OnLoad()
функции, виртуальная машина предполагает, что динамической библиотеке нужны толькоJNI_VERSION_1_1
Версии достаточно, но эта версия слишком старая, и многие новые функции недоступны, поэтому лучше предоставить эту функцию в динамической библиотеке и вернуть более новую версию, напримерJNI_VERSION_1_6
.
Функция регистрации
JNI_OnLoad()
Функция часто используется для некоторой инициализации, здесь выполняется «Динамическая регистрация».
«Динамическую регистрацию» можно сделать, позвонив_JNIEnv
структурныйRegisterNatives()
функция
struct _JNIEnv {
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
jint nMethods)
{ return functions->RegisterNatives(this, clazz, methods, nMethods); }
#endif /*__cplusplus*/
}
Фактически используемый прототип функции выглядит следующим образом.
jint RegisterNatives(JNIEnv *env, jclass clazz,
const JNINativeMethod *methods, jint nMethods);
Описание параметра
-
env
:JNIEnv
указатель, мыJavaVM и JNIEnv, соединяющие мир Javaпредставлено в статье. -
clazz
: представляет класс в Java. -
methos
: представляет структуруJNINativeMethod
множество.JNINativeMethod
Структура определяет уровень Javanative
Отношение отображения между методами и базовыми функциями. -
nMethods
: представляет третий параметрmethods
Размер массива, на который указывает.
возвращаемое значение
0 означает успех, отрицательное значение означает неудачу.
второй параметр
jcalss clazz
Как его получить, будет объяснено в следующем примере.
Структура JNINativeMethod
RegisterNatives()
Самая важная часть функцииJNINativeMethod
Эта структура, давайте посмотрим на объявление структуры
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
name
выражатьJava
изnative
имя метода.
signature
Представляет сигнатуру метода.
fnPtr
указатель на функцию, который указывает наJNI
Функция слоя, т.е.Java
слоистыйnative
Функция для создания отношения сопоставления.
Итак, как указать эти три параметра? Сначала я учу вас ленивому методу, мы находимся вВыполняйте разработку JNI без использования IDEиспользоватьjavah
Команда сгенерировала заголовочный файл, а прототип функции выглядит следующим образом.
/*
* Class: com_bxll_jnidemo_Hello
* Method: helloFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_bxll_jnidemo_Hello_helloFromJNI
(JNIEnv *, jclass);
JNINativeMethod
в структуреname
Значение соответствует аннотацииmethod
ценность, то естьhelloFromJNI
.
JNINativeMethod
в структуреsignature
Значение соответствует аннотацииSignature
ценность, то есть()Ljava/lang/String;
.
JNINativeMethod
в структуреfnPtr
На какую функцию должен указывать указатель? Мы можем реализовать функцию и использовать этот прототип, но имя можем определить сами, и не забудьте его удалить.JNIEXPORT
а такжеJNICALL
.
Реализовать динамическую регистрацию
Со всеми предыдущими основами мы можем реализовать нашу собственную функцию динамической регистрации.
сначала сnative
методJava
Класс выглядит следующим образом
package com.bxll.jnidemo;
class Hello
{
native String helloFromJNI();
}
Тогда мы можем использоватьjavah
Команда создает заголовочный файл, чтобы помочь нам точно выполнить динамическую регистрацию.Прототип функции в сгенерированном заголовочном файле выглядит следующим образом.
/*
* Class: com_bxll_jnidemo_Hello
* Method: helloFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_bxll_jnidemo_Hello_helloFromJNI
(JNIEnv *, jclass);
По комментариям к заголовочному файлу и прототипу функции можно добиться следующей динамической регистрации
#include <jni.h>
static jstring
native_helloFromJNI(JNIEnv *env, jobject thiz) {
const char *hello = "Hello from C++";
return env->NewStringUTF(hello);
}
const JNINativeMethod methods[] = {
{"helloFromJNI", "()Ljava/lang/String;", (void *) native_helloFromJNI}
};
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
int jniVersion = -1;
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
// 找到com.bxll.jnidemo.Hello类,只不过参数需要把点号替换为下划线
jclass clazz_hello = env->FindClass("com/bxll/jnidemo/Hello");
if (env->RegisterNatives(clazz_hello, methods,
sizeof(methods) / sizeof(methods[0])) == JNI_OK) {
jniVersion = JNI_VERSION_1_6;
}
}
return jniVersion;
}
Заголовочный файл должен быть импортирован
jni.h
, этот заголовочный файлJNI
необходимо.
Увеличение эффективности работы
Я Android-разработчик, в проектах часто сталкиваюсь с разработкой функции внизу, а потом нужно пройтиJNI
Обеспечьте интерфейс для верхнего уровня, который в настоящее время требует быстрого определения.Java
изnative
функцию и реализовать «динамическую регистрацию», если вам нужно каждый раз использовать файлы заголовков для поддержки «динамической регистрации», то эффективность разработки действительно низкая. Так как же нам быстро написать все необходимое для «динамической регистрации»? тогда вам нужноJNI
Типы и подписи очень знакомы, я объясню в следующей статье и покажу, как реализовать «динамическую регистрацию» с максимальной скоростью.
Суммировать
Функция «динамическая регистрация» относительно проста, нужно только разобратьсяJNI_OnLoad()
а такжеRegisterNatives()
Просто используйте функцию. Что касается конкретных деталей, нам все еще нужно внимательно следовать примеру.