Динамическая регистрация функций JNI

Java

существуетВыполняйте разработку JNI без использования IDEВ этой статье мы использовали метод «статической регистрации», чтобы установить однозначное соответствие между собственными методами в мире Java и функциями в мире Native.

Метод «статической регистрации» действительно спасает нас от многих вещей, но у него есть и соответствующие недостатки.

  1. Когда нативный метод Java впервые вызывается, виртуальная машина ищет функцию соответствующего нативного уровня, которая немного влияет на эффективность выполнения. Если поиск найден, сопоставленные отношения будут установлены, и нет необходимости тратить время поиска в следующий раз.
  2. Имя функции исходного слоя слишком длинное. Формат имени неправильный.Java_包名_类名_方法名,НапримерJava_com_bxll_jnidemo_Hello_helloFromJNI.
  3. Слишком много хлопот и влияет на эффективность работы (личный опыт работы).

Есть «статическая регистрация» и, конечно, есть «динамическая регистрация», так в чем преимущества и недостатки по сравнению?

  1. «Динамическая регистрация» требует, чтобы мы вручную установили отношения сопоставления функций, хотя это увеличивает объем кода, но может повысить эффективность работы.
  2. «Динамическая регистрация» позволяет нам настраивать имена функций.
  3. По сравнению со "статической регистрацией" эффективность работы выше.

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

Загрузить динамическую библиотеку

мы знаем, что в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);

Описание параметра

  1. env: JNIEnvуказатель, мыJavaVM и JNIEnv, соединяющие мир Javaпредставлено в статье.
  2. clazz: представляет класс в Java.
  3. methos: представляет структуруJNINativeMethodмножество.JNINativeMethodСтруктура определяет уровень JavanativeОтношение отображения между методами и базовыми функциями.
  4. 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()Просто используйте функцию. Что касается конкретных деталей, нам все еще нужно внимательно следовать примеру.