当前位置:网站首页>NDK series (6): let's talk about the way and time to register JNI functions

NDK series (6): let's talk about the way and time to register JNI functions

2022-07-28 08:44:00 Pengxurui

Please like and pay attention , Your support means a lot to me .

Hi, I'm Xiao Peng . This paper has included GitHub · Android-NoteBook in . Here you are Android Advanced growth knowledge system , Have like-minded friends , Official account [ Peng XURUI ] Take you to build your core competitiveness .

Preface

In the last article , We mentioned registration JNI function ( establish Java native Methods and JNI Mapping of functions ) There are two ways : Static registration and dynamic registration . Today, let's talk about this in detail 2 The usage and implementation principle of three registration methods .


This article is NDK Series article No 6 piece , Column list :

One 、 Language foundation :

  • 1、NDK Learning route : How to learn & My experience
  • 2、C Language foundation
  • 3、C ++ Language foundation
  • 4、C/C++ The build process : From source code to program running

Two 、NDK Development :

3、 ... and 、 The basic theory

Four 、 Fundamentals of computer


1. Static registration JNI function

1.1 Static registration usage

Static registration is based on 「 Appointment 」 Naming rules , adopt javah Can be generated automatically native Function declaration corresponding to method (IDE Intelligent generation , There is no need to execute commands manually ). for example :

HelloWorld.java

package com.xurui.hellojni;

public class HelloWorld {
    
    public native void sayHi();
}

Carry out orders :javac -h . HelloWorld.java( take javac and javah Merge ), Corresponding JNI function :

com_xurui_hellojni_HelloWorld.h

...
JNIEXPORT void JNICALL Java_com_xurui_hellojni_HelloWorld_sayHi
(JNIEnv *, jobject);
...

The naming rules of static registration are divided into 「 No overload 」 and 「 There are heavy loads 」2 In this case : When there is no heavy load 「 Short name 」 The rules , When there is heavy load 「 long name 」 The rules .

  • Short name rule (short name):Java_[ The fully qualified name of the class ( Underlined )]_[ Method name ] , Among the fully qualified names of classes . Change it to _ ;
  • Long name rule (long name): Add two underscores to the short name (__) And parameter descriptor , To distinguish function overloading .

Here we explain why we need to splice parameter descriptors when overloading ? because C Language has no function overloading , Function overloads cannot be distinguished by parameters , That's why you need to splice suffixes to eliminate overloading .

1.2 Principle analysis of static registration

Now? , Let's analyze the static registration matching JNI The execution of the function . Because there is no directly related information and function call entry , I was doing loadLibrary() load so The execution process of the library is to analyze clues , Finally, it is oriented to FindNativeMethod() This method , Judging from the content, it should be right .

java_vm_ext.cc

//  List of shared libraries 
std::unique_ptr<Libraries> libraries_;

//  Search for  native  Corresponding  JNI  Function pointer to function 
void* FindNativeMethod(Thread* self, ArtMethod* m, std::string& detail) {
    

    // 1、 obtain  native  The short name and long name corresponding to the method 
    std::string jni_short_name(m->JniShortName());
    std::string jni_long_name(m->JniLongName());

    // 2、 In the loaded  so  Search in the library 
    void* native_code = FindNativeMethodInternal(self,
                                                 declaring_class_loader_allocator,
                                                 shorty,
                                                 jni_short_name,
                                                 jni_long_name);
    return native_code;
}

// 2、 In the loaded  so  Search in the library 
void* FindNativeMethodInternal(Thread* self,
                               void* declaring_class_loader_allocator,
                               const char* shorty,
                               const std::string& jni_short_name,
                               const std::string& jni_long_name) {
    
    for (const auto& lib : libraries_) {
    
        SharedLibrary* const library = lib.second;

        // 2.1  Check whether it is the same  ClassLoader
        if (library->GetClassLoaderAllocator() != declaring_class_loader_allocator) {
    
            continue;
        }

        // 2.2  Search the short name first 
        const char* arg_shorty = library->NeedsNativeBridge() ? shorty : nullptr;
        void* fn = dlsym(library, jni_short_name)

        // 2.3  Then search for long names 
        if (fn == nullptr) {
    
            fn = dlsym(library, jni_long_name)
        }
        if (fn != nullptr) {
    
            return fn;
        }
    }
    return nullptr;
}

art_method.cc

// 1、 obtain  native  The short name and long name corresponding to the method 

//  Short name 
std::string ArtMethod::JniShortName() {
    
    return GetJniShortName(GetDeclaringClassDescriptor(), GetName());
}

//  long name 
std::string ArtMethod::JniLongName() {
    
    std::string long_name;
    long_name += JniShortName();
    long_name += "__";

    std::string signature(GetSignature().ToString());
    signature.erase(0, 1);
    signature.erase(signature.begin() + signature.find(')'), signature.end());

    long_name += MangleForJni(signature);

    return long_name;
}

descriptors_names.cc

std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
    
    //  Here is the calculation logic of short name 
}

The code above is very simplified , The main process is as follows :

  • 1、 Calculation native Short and long names of methods ;
  • 2、 Define native Class loader of method class , In the loaded so library libraries_ Mid search JNI function . If it is not loaded in advance so library , Naturally, you cannot search , Will throw out UnsatisfiedLinkError abnormal ;
  • 3、 Establish internal data structure , establish Java native Methods and JNI Mapping relationship of function pointer of function ;
  • 4、 Subsequent calls native Method , Then directly call the recorded function pointer .

About loading so The process of the library is 4、so File loading process analysis This article talks about , The loaded shared library is stored in libraries_ In the table .


2. Dynamic registration JNI function

Static registration is called for the first time Java native Method to search for the corresponding JNI function , Dynamic registration is to manually establish the mapping relationship in advance , And do not need to comply with static registration JNI Function naming rules .

2.1 Dynamic registration usage

Dynamic registration requires RegisterNatives(...) function , Its definition is jni.h In file :

jni.h

struct JNINativeInterface {
    
		//  register 
		//  Parameter two :Java Class  Object representation 
		//  Parameter 3 :JNINativeMethod  Array of structs 
		//  Parameter 4 :JNINativeMethod  Structure array length 
		jint        (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);
		//  Cancellation 
		//  Parameter two :Java Class  Object representation 
    jint        (*UnregisterNatives)(JNIEnv*, jclass);
};

typedef struct {
    
    const char* name;      // Java  Method name 
    const char* signature; // Java  Method Descriptor 
    void*       fnPtr;     // JNI  A function pointer 
} JNINativeMethod;

The sample program

//  Need to register  Java  Layer class name 
#define JNIREG_CLASS "com/xurui/MainActivity"

// JNINativeMethod  Array of structs 
static JNINativeMethod gmethod[] = {
    
        {
    "onStart","()I",(void*)onStart}, // JNINativeMethod  Structure 
};

//  load  so  Library callback 
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
    
        return -1;
    }
    assert(env != NULL);

		//  Perform dynamic registration 
    if (!registerNatives(env)) {
    
        return -1;
    }

    result = JNI_VERSION_1_6;

    return result;
}

//  Dynamic registration 
static int registerNatives(JNIEnv* env)
{
    
		//  Call the tool method to complete the registration 
    if (!registerNativeMethods(env,JNIREG_CLASS, gmethod, sizeof(gmethod) / sizeof(gmethod[0])))
        return JNI_FALSE;

		// JNINativeMethod  Array of structs 
    JNINativeMethod methods[] = {
    
            {
    "onStop","()V",(void*)onStop}, // JNINativeMethod  Structure 
    };
		//  Call the tool method to complete the registration 
    if (!registerNativeMethods(env,JNIREG_CLASS, smethod,sizeof(smethod) / sizeof(smethod[0])))
        return JNI_FALSE;
    return JNI_TRUE;
}

//  A tool method 
static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gmethod, int numMethods)
{
    
		//  Get... Based on the class name  jclass  object 
    jclass clazz = env->FindClass(className);
    if (clazz == NULL) {
    
        return JNI_FALSE;
    }
		//  call  RegisterNatives()
    if (env->RegisterNatives(clazz, gmethod, numMethods) < 0) {
    
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

The code is not complicated , A brief explanation :

  • 1、registerNativeMethods It's just a utility function , To simplify the FindClass This step ;
  • 2、methods It's a JNINativeMethod Array of structs , Defined Java native Methods and JNI Mapping of functions , Need record Java Method name 、Java Method Descriptor 、JNI A function pointer ;
  • 3、RegisterNatives Function is the final registration function , Delivery required jclass、JNINativeMethod Structure array and array length .

2.2 Principle analysis of dynamic registration

RegisterNatives The essence of the method is to specify the mapping relationship directly through the structure , Instead of waiting for the call native Method when searching JNI A function pointer , Therefore, dynamically registered native Method calls are more efficient . Besides , It can also reduce generation so Number of exported symbols in the library file , Can optimize so Volume of library file . For more information see Android Yes so Exploration and practice of volume optimization in “ Compact dynamic symbol table ” chapter .


3. register JNI The timing of the function

To sum up, register JNI The timing of the function , It is mainly divided into 3 Kind of :

When to register Registration method describe
1、 In the first call to this native When the method is used Static registration The virtual opportunity is JNI Search the function pointer in the function library and record it , Subsequent calls do not require repeated search
2、 load so library Dynamic registration load so The library will automatically call back JNI_OnLoad function , Call in it RegisterNatives register
3、 Register in advance Dynamic registration In the load so Behind the library , Call the native Before the method , Registered statically native Function trigger RegisterNatives register . For example, in App Startup time , Many system source codes will be registered in advance

With Android Virtual machine source code as an example : stay App Process startup process , It will be executed once after the virtual machine is created JNI Function registration . We're in a lot of Framework You can see... In the source code native Method , But the call was not found System.loadLibrary(...) The place of , In fact, the registration is completed when the virtual machine starts .

AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) {
    
    ...
    if (startReg(env) < 0) {
    
        ALOGE("Unable to register all android natives\\n");
    }
    ...
}

// start -> startReg:
int AndroidRuntime::startReg(JNIEnv* env) {
    
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
    env->PushLocalFrame(200);
		//  perform  JNI  register 
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
    
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);
    return 0;
}

// startReg->register_jni_procs:
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) {
    
		//  Traversing a two-dimensional array 
    for (size_t i = 0; i < count; i++) {
    
        //  perform  JNI  register 
        if (array[i].mProc(env) < 0) {
    
            return -1;
        }
    }
    return 0;
}

// JNINativeMethod  Two dimensional array of structure array 
static const RegJNIRec gRegJNI[] = {
    
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
    REG_JNI(register_android_os_SystemClock),
    REG_JNI(register_android_util_EventLog),
    ...
}

struct RegJNIRec {
    
    int (*mProc)(JNIEnv*);
}

//  The return value is  JNINativeMethod  Array of structs 
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
    
    const JNINativeMethod methods[] = {
    
        {
     "nativeFinishInit", "()V",
            (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
        {
     "nativeSetExitWithoutCleanup", "(Z)V",
            (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
    };
    return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
        methods, NELEM(methods));
}

4. summary

Summarize the difference between static registration and dynamic registration :

  • 1、 Static registration establishes mapping relationships based on naming conventions , Dynamic registration passes JNINativeMethod Structure to establish mapping relationship ;
  • 2、 Static registration is the first time the native Method to search and establish mapping relationship , Dynamic registration will call this native Establish mapping relationship before method ;
  • 3、 Static registration requires all JNI Function exposed to dynamic symbol table , Dynamic registration does not need to be exposed to the dynamic symbol table , Can be streamlined so The file size .

Reference material

Your praise means a lot to me ! WeChat search official account [ Peng XURUI ], I hope you can discuss technology , Find like-minded friends , I'll see you next time !

原网站

版权声明
本文为[Pengxurui]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/209/202207280715253407.html