Getting started with JNI & NDK in Android (3) Dynamic registration of Native functions

1 Introduction

In the previous two articles, JNI has been introduced. Now let's review, it mainly generates a .h header file by using the javac -h command to generate the registration association of both Java and Native method letters. In this way, when the Native method is executed in the Java code, it will find the real implementation of these Natives through the associated mapping relationship on both sides. In fact, JNI has two ways to associate Native methods, namely static registration and dynamic registration.

2 Registration method

2.1 Static registration

The Demo listed in the previous article uses the command to generate the .h header file by using the static registration method. It can be found that in the .h header file generated in this way, the name of the function is regular, such as: Java_com_zyx_jnidemo_JNIUtils_getInfo, the law is: Java prefix + full name of the class where the Native method is located (replace with _) + Native method name.

The advantage of static registration is that it is convenient for automatic generation operation, but the disadvantage is also quite obvious, that is, the letter name is too long and not flexible enough.

2.2 Dynamic registration

Dynamic registration is to dynamically associate the methods and functions of the Java layer and the Native layer through the RegisterNatives function, and there is no need to follow a specific method naming format, making the code more flexible.

When we use the System.loadLibarary () method in the Java code to load the so file, the Java virtual machine will call the JNI_OnLoad function. The function of this function is to tell the virtual machine which version of JNI should be used. If we do not specify, It uses JNI 1.1 version by default. You can also do some initialization events in the JNI_OnLoad function, and its corresponding anti-load function is JNI_OnUnload. So if we want to register the Native function dynamically, the best time is to execute it in the JNI_OnLoad function.

4 Continue to modify Hello World

Continue to use the Demo in the previous article, this time we do not generate the .h header file through the command, and create a new .h header file JNIUtils.h by ourselves , as follows:

/* 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

Modify the JNIUtils.cpp file:

#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;
}

It is also very simple to modify, and then recompile the program, running it can be seen:

 

Description:

Two functions and an array are defined in JNIUtils.h :

getInfoToNative Native layer corresponds to the function to be registered in the Java layer

JNI_OnLoad to initialize and specify the JNI version

gJniNativeMethods is an array of JNINativeMethod type, it is a function is a mapping table, used to associate the Native method defined in the Java layer and the corresponding function of the Native layer, where the first parameter is the method name in Java, the second parameter is The signature of the method, the third parameter is a pointer to the function in Native

The two defined functions are implemented in JNIUtils.cpp. It can be seen that the RegisterNatives function is used inside the JNI_OnLoad function to pass in the class and gJniNativeMethods array in Java to perform dynamic registration.

 

Click to download example

 

Published 106 original articles · praised 37 · 80,000 views

Guess you like

Origin blog.csdn.net/lyz_zyx/article/details/88690930