JNI Native dynamic registration drill

preface:

A few days ago, I shared an article about TS stream parsing. A friend asked, why don't you use dynamic registration? What is JN dynamic registration? Today, I'll introduce it to you.

1, Introduction to JNI Native registration

There are mainly two kinds of jni native registration: static registration and dynamic registration. Among them, static registration is commonly used by us, because there are few jni interfaces used in some projects. It can be realized easily and quickly through static registration. However, when there are more interfaces, it will be a little troublesome. In addition, the package name association of static registration can easily lead to errors and poor typesetting, Dynamic registration solves this problem.

  1. Static registration:
    • Features: fast implementation, but the function name is also very long, which is not suitable for management

    • Its compiled form is to traverse according to the function name, find the association between java and jni functions, and then call statically

        extern "C" JNIEXPORT jstring JNICALL
        Java_com_blur_blurbyjnidemo_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {
            std::string hello = "Hello from C++";
            return env->NewStringUTF(hello.c_str());
        }
      
  2. Dynamic registration:
    • Features: each method is mapped one by one, which is simple and clear, not easy to make mistakes and easy to manage

    • It registers the jni function mapping table in the jvm, and then calls the function with the corresponding name and parameters according to the function mapping table,

            /*
             * used in RegisterNatives to describe native method name, signature,
             * and function pointer.from jni.h
             */
            typedef struct {
                const char* name;//java method name
                const char* signature;//Description values of function parameters and return values corresponding to jni. Refer to section 3 for details
                void* fnPtr;//fnPtr is a function pointer to the C function
            } JNINativeMethod;
      

2, Specific examples of dynamic registration

  1. Several steps of dynamic registration:

    • Bind the corresponding method mapping table
    • At JNI_ Register methods in the mapping table when onload
    • Here is the demo code

    native-lib.cpp:

         #include <jni.h>
         #include "mDebug.h"
         #include <string>
         #ifndef NELEM
         #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
         #endif
         //c test method
         static jstring j_hello(JNIEnv* env, jobject thiz )
         {
                  const char* hello  = "Hello from C++";
                  return env->NewStringUTF(hello);
         }
         static jstring j_hello2(JNIEnv* env, jobject thiz )
         {
                  const char* hello  = "test-----------------------------2";
                  return env->NewStringUTF(hello);
         }
         
         /**
          * 1. Bind the corresponding method mapping table
          */
         static const JNINativeMethod jniMethods[] = {
                 {"stringFromJNI", "()Ljava/lang/String;", (void*)j_hello},
                 {"stringFromJNI2", "()Ljava/lang/String;", (void*)j_hello2},
         };
         /**Registration method*/
         static int  registerMethods(JNIEnv * env, const char* className ,const JNINativeMethod* gMethods, int numMethods) {
                  jclass clazz;
                  clazz =env->FindClass( className);
                  if (clazz == NULL) {
                           return JNI_FALSE;
                  }
                  if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
                           return JNI_FALSE;
                  }
                  return JNI_TRUE;
         }
         
         /**
          * Register all methods
          */
         static int registerAllMethods(JNIEnv* env) {
                  const char* kClassName = "com/blur/blurbyjnidemo/NativeLib";//Specify the class to register
                  return registerMethods(env, kClassName,jniMethods,  NELEM(jniMethods));
         }
         
         /***
          * 2. jni_onload Register methods in the mapping table when loading
          */
         JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
                  JNIEnv* env = NULL;
                  if (vm->GetEnv( (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
                           return -1;
                  }
                  mInfo("JNI_OnLoad");
                  if (!registerAllMethods(env)) {//Register all methods
                           return -1;
                  }
                  return JNI_VERSION_1_4;
         }
    

    mDebug.h:

         #ifndef __MDEBUG_H__
         #define __MDEBUG_H__
         #include <jni.h>
         #include <android/log.h>
         #ifndef BASETYPES
         #define BASETYPES
         
         //typedef _Null_terminated_ char *PSZ;
         #endif  /* !BASETYPES */
         #ifndef CAMERA_LOG_TAG
         #define CAMERA_LOG_TAG  "debug"
         #define mDebug(format, ...) __android_log_print(ANDROID_LOG_ERROR,CAMERA_LOG_TAG, format" [File:%s, Line:%d, Function:%s]",##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__)
         #define mInfo(format, ...)  __android_log_print(ANDROID_LOG_INFO,CAMERA_LOG_TAG, format" [File:%s, Line:%d, Function:%s]",##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__)
         #define mMsg(...)  __android_log_print(ANDROID_LOG_INFO,CAMERA_LOG_TAG, __VA_ARGS__)
         #endif
         
         #endif
    

    NativeLib.java:

         package com.blur.blurbyjnidemo;
         public class NativeLib {
                  // Used to load the 'native-lib' library on application startup.
                  static {
                           System.loadLibrary("native-lib");
                  }
         
                  /**
                   * A native method that is implemented by the 'native-lib' native library,
                   * which is packaged with this application.
                   */
                  public  native String stringFromJNI();
                  public static native String stringFromJNI2();
         }
    

3, jni corresponding parameter mapping table

For the second parameter of the above dynamic registration structure, you need to fill in the mapping value returned by the parameter and method:

The structure of this string is a bracket followed by a string:

1. The description in "()" is the description of the incoming parameters of the function

2. Parentheses are followed by the return value description
For example: "(V)": void function(); (JF)Z ": indicates boolean function(long l,float f); etc
Basic type cross reference table

    V      void            void
    Z       jboolean     boolean
    I        jint              int
    J       jlong            long
    D      jdouble       double
    F      jfloat            float
    B      jbyte            byte
    C      jchar           char
    S      jshort          short

    Array preceded by"[",For example:
        [I       jintArray      int[]

For non basic types, such as class types, start with "L", separate the package name and class name with "/", and start with " Ending, for example:

    Ljava/lang/String; String jstring

The above is the normal use steps of JNI dynamic registration. Of course, it can be encapsulated more specifically in the project. Mastering dynamic registration is a required course for large-scale project development~

Keywords: Java C++ JNI

Added by prbrowne on Sat, 05 Feb 2022 14:18:55 +0200