Android JNI and NDK learning (three): dynamic registration

Overview

When the native method of java is executed, how does the virtual machine know which method to call so? This requires registration. By registering, you can bind the java method and the so method together, so that you can find the corresponding method. This article is only a note, in case you forget it in the future

There are two kinds of ways that is registered 静态注册and动态注册

Static registration

The project that we automatically generated before is statically registered, let's look at the code

extern "C" JNIEXPORT jstring JNICALL
Java_com_text_ndk1_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    
    
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

When the native method of java is called for the first time, the virtual machine searches for the corresponding native function, if it exists, an association is established, and when it is called again later, this part of the operation will be completed by the virtual machine

Corresponding rules

Java+package name+class name+method name

Which is divided by underscores, and package names are also divided by underscores

This will have some disadvantages:

  • Name is too long
  • The first call needs to search, which affects efficiency

Dynamic registration

  • We need to establish contact manually, which increases the amount of code but improves efficiency
  • Allow you to define function names yourself

Load dynamic library

The java layer System.loadLibrary()can load a dynamic library through methods, at which time the virtual machine will call the JNI_OnLoad()functions in the jni library

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

The return value represents the jni version required by the dynamic library. If the virtual machine does not recognize this version, then the dynamic library cannot be loaded

The current return values ​​are,JNI_VERSION_1_1, JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6。

If the library does not provide dynamic JNI_OnLoad()function will use the default JNI_VERSION_1_1version, but this version is too old, not a lot of new functions, the best return version

Register function

JNI_OnLoad()Functions are often used to do some initialization operations, and dynamic registration is performed here

Dynamic registration is completed by _JNIEnvthe RegisterNatives()function

The function prototype is

jint RegisterNatives(JNIEnv *env, jclass clazz, 
        const JNINativeMethod *methods, jint nMethods);
  • The first parameter: JNIEnv * pointer
  • The second parameter: represents a java class
  • The third parameter: represents the JNINativeMethodstructure array, which JNINativeMethoddefines the mapping relationship between java layer functions and native layer functions
  • The fourth parameter: represents the size of the third parameter methods array

Return value 0 means success, negative value means failure

JNINativeMethod structure

RegisterNativesThe most important thing about the function is the JNINativeMethodstructure. Let's take a look at this structure.

typedef struct {
    
    
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;
  • name: represents the name of the native method of java
  • signature: the signature of the method
  • fnPtr: is a function pointer that points to a function of the jni layer, that is, the function that establishes a connection between the java layer and the native layer

practice

Now we have a java class with two native methods. Let’s complete the dynamic registration of these two methods.

public class TextJni {
    
    

    static {
    
    
        System.loadLibrary("textjni_lib");
    }

    native int text(String message);

    static native int static_text(String message);
}

First, manually write the corresponding method of C

jint native_text(JNIEnv *env, jobject jobject1, jstring msg) 

jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg) 

native_textThe textmethod native_staic_textcorresponding to the java layer, the static_textmethod corresponding to the java layer

So how is it written?

  • Native method name, this can be started at will
  • The return value corresponds to that in java, int in java, jni is jint
  • The first parameter is fixed is the JNIEnv pointer
  • The second parameter is currently understood as a fixed jobject
  • The following parameters are converted from java parameters

Then need to fillJNINativeMethod

static const JNINativeMethod nativeMethod[] = {
    
    
        {
    
    "text",        "(Ljava/lang/String;)I", (void *) native_text},
        {
    
    "static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};

I have already talked about this above

Then start registration

static int registNativeMethod(JNIEnv *env) {
    
    
    int result = -1;
    //找到native方法所在的class
    jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJni");
    if (env->RegisterNatives(class_text, nativeMethod,
                             sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
    
    
        result = 0;
    }
    return result;
}

Then the dynamic registration is successful

Look at the complete code

java code

public class TextJni {
    
    

    static {
    
    
        System.loadLibrary("textjni_lib");
    }

    native int text(String message);

    static native int static_text(String message);
}

C code

#include <jni.h>
#include <string>
#include <android/log.h>

jint native_text(JNIEnv *env, jobject jobject1, jstring msg) {
    
    
    const char *p_msg = env->GetStringUTFChars(msg, JNI_FALSE);
    __android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);

    return 0;
}

jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg) {
    
    
    const char *p_msg = env->GetStringUTFChars(meg, JNI_FALSE);
    __android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);

    return 0;
}


static const JNINativeMethod nativeMethod[] = {
    
    
        {
    
    "text",        "(Ljava/lang/String;)I", (void *) native_text},
        {
    
    "static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};

static int registNativeMethod(JNIEnv *env) {
    
    
    int result = -1;

    jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJni");
    if (env->RegisterNatives(class_text, nativeMethod,
                             sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
    
    
        result = 0;
    }
    return result;
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    
    
    JNIEnv *env = NULL;
    int result = -1;

    if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
    
    
        if (registNativeMethod(env) == JNI_OK) {
    
    
            result = JNI_VERSION_1_6;
        }
        return result;
    }
}

transfer

public class MainActivity extends AppCompatActivity {
    
    

    // Used to load the 'native-lib' library on application startup.
    static {
    
    
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextJni.static_text("我是静态方法,哈哈");
        new TextJni().text("我是普通方法,哈哈");
    }

   
}

Look at the print information

mmm: method = native_staic_text, msg = 我是静态方法,哈哈
mmm: method = native_text, msg = 我是普通方法,哈哈

reference

https://juejin.im/post/5d1f16f16fb9a07ee46382e1#heading-9

https://blog.csdn.net/afei__/article/details/81031965

https://www.jianshu.com/p/b71aeb4ed13d

Guess you like

Origin blog.csdn.net/qq_34760508/article/details/106529011