Introducción a JNI y NDK en Android (3) Registro dinámico de funciones nativas

1 Introducción

En los dos artículos anteriores, JNI ha sido introducido. Ahora revisemos, genera principalmente un archivo de encabezado .h usando el comando javac -h para generar la asociación de registro de las letras de los métodos Java y Native. De esta manera, cuando el método nativo se ejecuta en el código Java, encontrará la implementación real de estos nativos a través de la relación de mapeo asociada en ambos lados. De hecho, JNI tiene dos formas de asociar métodos nativos, a saber, el registro estático y el registro dinámico.

2 Método de registro

2.1 Registro estático

La demostración enumerada en el artículo anterior utiliza el comando para generar el archivo de encabezado .h mediante el método de registro estático. Se puede encontrar que en el archivo de encabezado .h generado de esta manera, el nombre de la función es regular, como: Java_com_zyx_jnidemo_JNIUtils_getInfo, la ley es: prefijo Java + nombre completo de la clase donde se encuentra el método nativo (reemplazar con _) + nombre del método nativo.

La ventaja del registro estático es que es conveniente para la operación de generación automática, pero la desventaja también es bastante obvia, es decir, el nombre de la letra es demasiado largo y no lo suficientemente flexible.

2.2 Registro dinámico

El registro dinámico consiste en asociar dinámicamente los métodos y funciones de la capa Java y la capa nativa a través de la función RegisterNatives, y no es necesario seguir un formato de denominación de método específico, lo que hace que el código sea más flexible.

Cuando usamos el método System.loadLibarary () en el código Java para cargar el archivo so, la máquina virtual Java llamará a la función JNI_OnLoad. La función de esta función es decirle a la máquina virtual qué versión de JNI debe usarse. Si no especificamos, Utiliza la versión JNI 1.1 de forma predeterminada. También puede hacer algunos eventos de inicialización en la función JNI_OnLoad, y su correspondiente función anti-carga es JNI_OnUnload. Entonces, si queremos registrar la función nativa dinámicamente, el mejor momento es ejecutarla en la función JNI_OnLoad.

4 Continuar modificando Hello World

Continúe usando la demostración en el artículo anterior, esta vez no generamos el archivo de encabezado .h a través del comando, y creamos un nuevo archivo de encabezado .h JNIUtils.h por nosotros mismos , de la siguiente manera:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_zyx_jnidemo_JNIUtils */

#ifndef _Included_com_zyx_jnidemo_JNIUtils
#define _Included_com_zyx_jnidemo_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     com_zyx_jnidemo_JNIUtils
 * Method:    getInfo
 * Signature: ()Ljava/lang/String;
 */
//JNIEXPORT jstring JNICALL Java_com_zyx_jnidemo_JNIUtils_getInfo
//  (JNIEnv *, jclass);


/*
 * Native层对应Java层中的方法
 */
static jstring getInfoToNative(JNIEnv * env, jclass thiz);

/*
 * Java中所定义的Native方法映射表
 */
static JNINativeMethod gJniNativeMethods[] = {
        {"getInfo", "()Ljava/lang/String;", (void*)getInfoToNative},
};

/*
 * 初始化
 */
jint JNI_OnLoad(JavaVM* vm, void* reserved);


#ifdef __cplusplus
}
#endif
#endif

Modifique el archivo JNIUtils.cpp :

#include "JNIUtils.h"
#include <stdio.h>
#include <android/log.h>

//JNIEXPORT jstring JNICALL Java_com_zyx_jnidemo_JNIUtils_getInfo(JNIEnv * env, jclass thiz) {
//    __android_log_print(ANDROID_LOG_DEBUG, "zyx", "Hello world from JNI !");
//    //return env->NewStringUTF("Hello world from JNI !");
//
//    jclass clazz = env->FindClass("com/zyx/jnidemo/JNIUtils");
//    if (clazz == NULL) {
//        return env->NewStringUTF("find class error");
//    }
//    jmethodID methodId = env->GetStaticMethodID(clazz, "getJavaInfo", "(Ljava/lang/String;I)Ljava/lang/String;");
//    if(methodId == NULL) {
//        return env->NewStringUTF("find method error");
//    }
//    jstring info = env->NewStringUTF("Hello world from JNI !");
//    jint index = 2;
//    return (jstring)env->CallStaticObjectMethod(clazz, methodId, info, index);
//}

static jstring getInfoToNative(JNIEnv * env, jclass thiz) {
    return env->NewStringUTF("Hello world from JNI !3");
}

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

    JNIEnv* jniEnv = NULL;

    // 从虚拟机中获得JNIEnv,同时指定jni版本
    if (vm->GetEnv((void**) &jniEnv, JNI_VERSION_1_6) != JNI_OK || jniEnv == NULL) {
        return JNI_ERR;
    }

    // 获得Java代码中Natives方法所在类
    jclass clazz = (jniEnv)->FindClass("com/zyx/jnidemo/JNIUtils");
    if (clazz == NULL) {
        return JNI_ERR;
    }

    // 执行注册
    jint nMethods = sizeof(gJniNativeMethods) / sizeof(JNINativeMethod);
    if ((jniEnv)->RegisterNatives(clazz, gJniNativeMethods, nMethods) < 0) {
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}

También es muy simple de modificar y luego recompilar el programa, ejecutándolo se puede ver:

 

Descripción:

Dos funciones y una matriz se definen en JNIUtils.h :

La capa nativa getInfoToNative corresponde a la función que se registrará en la capa Java

JNI_OnLoad para inicializar y especificar la versión JNI

gJniNativeMethods es una matriz del tipo JNINativeMethod, es una función es una tabla de mapeo, utilizada para asociar el método nativo definido en la capa Java y la función correspondiente de la capa nativa, donde el primer parámetro es el nombre del método en Java, el segundo parámetro es La firma del método, el tercer parámetro es un puntero a la función en Native

Las dos funciones definidas se implementan en JNIUtils.cpp. Se puede ver que la función RegisterNatives se usa dentro de la función JNI_OnLoad para pasar las clases y las matrices gJniNativeMethods en Java para realizar el registro dinámico.

 

Haga clic para descargar el ejemplo

 

106 artículos originales publicados · elogiados 37 · 80,000 visitas

Supongo que te gusta

Origin blog.csdn.net/lyz_zyx/article/details/88690930
Recomendado
Clasificación