Zygote -- fertilized egg of java World in Android system (I. zygote in C/C + +)

Keywords: Java Android

0. Introduction

        The underlying kernel of Android is built based on Linux, which is in the Native world, while the upper application of Android belongs to the Java world. So how did the system hatch the Java world from Native during the startup of the Android system? This is the main responsibility of Zygote, the protagonist of this article.

         The Android system version selected in this article is 9.0 Pie. All code fragment paths in this article have been marked on the first line of the code block. The purpose of this article is to record your learning process and experience, and do not do business or profit. All big man blogs or works learned or quoted in the learning process will be marked as much as possible. Thank you for sharing. This paper draws lessons from the following:

1. Zygote in C/C + +

        Last article Learning the init startup process of Android 9.0 (PIE) 1 process As mentioned in, init, the No. 1 process of the Android system, will start other processes through the. RC configuration file, and zygote is also started in this way. However, there are several zygote RC files in the pie\system\core\rootdir \ directory. Depending on the CPU configuration of the devices in the production environment, there are init.zygote32.rc and init.zygote32_64.rc,init.zygote64.rc,init.zygote64_32.rc these options. In the init.rc header, you can see that when importing the corresponding configured RC file, it is controlled according to the attribute ro.zygote. Because the attribute value of my production environment is zygote32, so   reference resources Init process startup and init.rc full resolution of Android system and Detailed explanation of init.rc file of Android source code   Take a look at init.zygote32.rc.

// pie\system\core\rootdir\init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main    //The class of this service is specified as main, which makes it easy to start or stop multiple services at the same time
    priority -20  //Refer to the process priority nice of linux. The value range is [- 20, 19]. The smaller the value, the higher the priority
    user root     //Switch the user name to root before executing this service
    group root readproc reserved_disk    //Similar to user, switch the group name
    socket zygote stream 660 root system    //Create a socket of unix domain type in the directory / dev/socket / of the target machine. The socket file is named zygote and the port is 660. Running the program requires root or system permissions
    onrestart write /sys/android_power/request_state wake    //When the service is restarted, write content to the specified file
    onrestart write /sys/power/state on
    onrestart restart audioserver    //When the service is restarted, restart the specified service
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks    //When a child process is created, the pid of the process is written to the file

1.1,app_process

         From the first line, we can see that zygote only renames the program, and actually runs the app under the target machine / system/bin directory_ Process program, followed by the parameters brought by running the program, that is, the app_process is a command-line program. First find the main function of the program. Since the function is long, let's divide the main code into several parts.

         1.1.1. Android runtime initialization

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
    if (!LOG_NDEBUG) {    //Print the parameters transmitted in the debug mode to facilitate debugging and locating problems
      String8 argv_String;
      for (int i = 0; i < argc; ++i) {
        argv_String.append("\"");
        argv_String.append(argv[i]);
        argv_String.append("\" ");
      }
      ALOGV("app_process main with argv: %s", argv_String.string());
    }

    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));    //Call the constructor of the parent class AndroidRuntime to initialize
    argc--;
    argv++;    // Ignore the first parameter argv[0] occupied by the program name

    ............
}

         AppRuntime here is inherited pie\frameworks\base\core\jni\AndroidRuntime.cpp. The constructor of AppRuntime class is empty, so go to call the constructor of parent class AndroidRuntime:

//    pie\frameworks\base\core\jni\AndroidRuntime.cpp
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
        mExitWithoutCleanup(false),       //Initialize the member variables of this class with the parameters passed in from the command line,
        mArgBlockStart(argBlockStart),    //The starting position of the first parameter,
        mArgBlockLength(argBlockLength)   //And the memory occupied by all parameter blocks
{
    init_android_graphics();              //Initialize android graphics
    mOptions.setCapacity(20);             //Some options are required as parameters when starting the virtual machine, and the number of options is set here
    assert(gCurRuntime == NULL);          //Null pointer detection is required for each process
    gCurRuntime = this;    
}

        1.1.2,spaced_commands        TODO

        This part is spaced_ The contents related to commands are a little confusing, but it does not affect future analysis, so put it first, and then add it when you understand it later. If there are leaders who know, you can also add it in the comments. Thank you very much.

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
............
 const char* spaced_commands[] = { "-cp", "-classpath" };
    bool known_command = false;

    int i;
    for (i = 0; i < argc; i++) {
        if (known_command == true) {
          runtime.addOption(strdup(argv[i]));
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
          if (strcmp(argv[i], spaced_commands[j]) == 0) {
            known_command = true;
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        ALOGV("app_process main add option '%s'", argv[i]);
    }
}

         1.1.3 parameter analysis

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
............
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused parameter "/ system/bin"
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {    //When resolving to parameter "-- zygote",
            zygote = true;                     //The description is zygote mode
            niceName = ZYGOTE_NICE_NAME;       //Prepare app_process alias ZYGOTE_NICE_NAME="zygote"
        } else if (strcmp(arg, "--start-system-server") == 0) {    //When parsing to parameter "-- start system server"
            startSystemServer = true;                              //System needs to be started_ server
        } else if (strcmp(arg, "--application") == 0) {    //If there is "-- application" in the parameter,
            application = true;                            //application mode
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);                      //Set alias in application mode
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);                          //Set class name in application mode
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {    //In non zygote mode, pass the parameter "application" to RuntimeInit
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);    //Pass the class name and remaining parameters

        if (!LOG_NDEBUG) {
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
        }
    } else {    //In zygote mode
        maybeCreateDalvikCache();                        //Create / data / Dalvik cache / directory
        if (startSystemServer) {
            args.add(String8("start-system-server"));    //Pass the parameter "start system server"
        }

        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {    //Read abi interface list
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);                 //Pass the CPU architecture type parameters supported by the system "-- ABI list = armeabi-v7a, armeabi,......"

        for (; i < argc; ++i) {
            args.add(String8(argv[i]));    //Pass remaining parameters
        }
    }
}

          For the parameters passed in from the main function, analyze the relevant parameters in zygote mode and application mode, set the class name, process alias, set ID, pass parameters to the Android runtime and other actions according to the parameters.

        1.1.4,runtime.start()

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
............
    if (!niceName.isEmpty()) {    
        runtime.setArgv0(niceName.string(), true);    //Set the alias of the process to the previously prepared niceName
    }

    if (zygote) {    //zygote mode
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);    //Call the start() function of the parent class AndroidRuntime
    } else if (className) {    //application mode
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

        This is mainly for app_ The process process sets the alias and then calls runtime.start() to call the start() function of the parent class AndroidRuntime, which we will look at later. The function calls at this location are shown in the following figure:

1.2,AndroidRuntime::start()

        1.2.1 environmental variables

        This function is still separated. The first part is relatively simple. It does some event printing, setting and detection related to environment variables.

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());
    static const String8 startSystemServer("start-system-server");
    bool primary_zygote = false;
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
            primary_zygote = true;
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));    //Print the startSystemServer event with time
        }
    }

    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /system does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);    //Setting environment variables
    }

    const char* artRootDir = getenv("ANDROID_ART_ROOT");    //Check environment variables
    if (artRootDir == NULL) {
        LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");
        return;
    }

    const char* i18nRootDir = getenv("ANDROID_I18N_ROOT");
    if (i18nRootDir == NULL) {
        LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable.");
        return;
    }

    const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");
    if (tzdataRootDir == NULL) {
        LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");
        return;
    }
    ............
}

        1.2.2. Load virtual machine Library

// // pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
............
JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    ............
}

          Next, you can see the JniInvocation class. The related definitions of this class are located in the pie/libnativehelper / directory. Its main function is to provide external interfaces for dynamically calling the internal interfaces of the virtual machine. First, look at the Init() function invoked in the code:

// pie\libnativehelper\JniInvocation.cpp
bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
  char buffer[PROP_VALUE_MAX];
#else
  char* buffer = NULL;
#endif
  library = GetLibrary(library, buffer);      //Get the default library libart.so
   //RTLD_NOW: the open mode of the dlopen function is to immediately resolve all undefined symbols. If not, NULL will be returned in dlopen
   //RTLD_NODELETE: the library is not unloaded during dlclose() because even in JNI_ After deletejavavm returns, some threads may not have finished exiting. If the library is unloaded, this may lead to segment errors  
  const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
  handle_ = dlopen(library, kDlopenFlags);    //Open the libart.so library and get the handle
  if (handle_ == NULL) {                      //If the opening fails, try again to ensure that libart.so is opened normally
    if (strcmp(library, kLibraryFallback) == 0) {
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
    ALOGW("Falling back from %s to %s after dlopen error: %s", library, kLibraryFallback, dlerror());
    library = kLibraryFallback;
    handle_ = dlopen(library, kDlopenFlags);
    if (handle_ == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
  }
  //FindSymbol() function obtains JNI by calling dlsym() function_ GetDefaultJavaVMInitArgs,JNI_CreateJavaVM,JNI_GetCreatedJavaVMs addresses of these three functions
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_), "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_), "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_), "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}

        It can be seen that the main function of Init function is to find and open the default virtual machine library, and obtain the pointers of several key functions in the library according to the open file handle. It should be noted that the default library obtained by GetLibrary() here is the library of virtual machines. In earlier versions, such as 4.4 kitkak, the dalvik virtual machine is used, so the default library obtained is libdvm.so. In later versions, there will be art virtual machines, so the default library is libart.so. The main difference between the two is that the art virtual machine advances the bytecode translation optimization from runtime to installation,   Exchange space for time to optimize the running and loading time.

         1.2.3 start JavaVM

// pie\libnativehelper\JniInvocation.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ............ 
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);    //Empty interface
    ............
}

        JNIEnv, which appears first, is a common thing in JNI programming. Its related definition is in the file pie\libnativehelper\include_jni\jni.h, so this is also a preparation for registering JNI functions later. Let's take a look at the startVm() function. Its main function is to create a Java VM. There are many addOption() operations in this function, which are intercepted here for description.

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{
    JavaVMInitArgs initArgs;
    ............
    addOption(***);    //A lot of actions added to vector < javavmoption > moptions through attribute values or configuration are omitted here
    ............

    //Assign a value to the member of the structure JavaVMInitArgs
    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;
    
    //Call the JNI_CreateJavaVM function in the libart.so library opened earlier to create a JavaVM.
    //JavaVM * is per process in essence, and JNIEnv * is per thread. If the creation here is successful, you can issue JNI calls.
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {   
        ALOGE("JNI_CreateJavaVM failed\n");
        return -1;
    }
    return 0;
}

        1.2.4 register JNI function

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

// pie\libnativehelper\JniInvocation.cpp
int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    //Set the function of creating threads in Threads.cpp to javaCreateThreadEtc
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");
    
    //Each registration function will return one or more native referenced objects. At this time, the virtual machine has not been fully started,
    //Therefore, first use the Frame to manage the life cycle of local references. The parameter 200 is to store enough space.
    env->PushLocalFrame(200);
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {    //JNI function registration action
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);

    return 0;
}

          reference resources android system core mechanism foundation (02) Thread class analysis , it can be seen that the androidSetCreateThreadFunc() function here is a function in pie\system\core\libutils\Threads.cpp. The action here is to set the thread creation function in the Threads class from androidCreateRawThreadEtc() to javaCreateThreadEtc() defined in AndroidRuntime.cpp. Although androidCreateRawThreadEtc() is finally called Function, but the thread function is changed to the javaThreadShell() function in androidruntime. CPP.

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
int AndroidRuntime::javaCreateThreadEtc(
                                android_thread_func_t entryFunction,
                                void* userData,
                                const char* threadName,
                                int32_t threadPriority,
                                size_t threadStackSize,
                                android_thread_id_t* threadId)
{
    void** args = (void**) malloc(3 * sizeof(void*));   //Remember to use free in the javaThreadShell() function
    int result;
    LOG_ALWAYS_FATAL_IF(threadName == nullptr, "threadName not provided to javaCreateThreadEtc");

    args[0] = (void*) entryFunction;
    args[1] = userData;
    args[2] = (void*) strdup(threadName);   // Remember to use free in the javaThreadShell() function

    //Finally, androidCreateRawThreadEtc() is called, but the thread function is set to javaThreadShell()
    result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,  
              threadName, threadPriority, threadStackSize, threadId);    
    return result;
}

// Thread function
int AndroidRuntime::javaThreadShell(void* args) {
    void* start = ((void**)args)[0];
    void* userData = ((void **)args)[1];
    char* name = (char*) ((void **)args)[2];
    free(args);    //Release the args of malloc in the javaCreateThreadEtc function above
    JNIEnv* env;
    int result;

    if (javaAttachThread(name, &env) != JNI_OK)    //Make the currently created thread visible to the VM
        return -1;
    result = (*(android_thread_func_t)start)(userData);    //Run the created thread

    javaDetachThread();    //When a thread exits, the current thread is separated from the thread set visible to the virtual machine
    free(name);

    return result;
}

        After analyzing the androidSetCreateThreadFunc() function, let's look down at the more important register_jni_procs() function. Its main function is to register JNI functions:

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    //The RegJNIRec array passed in is full of function pointers of some registered functions,
    //Therefore, executing the member mProc of RegJNIRec is equivalent to executing the corresponding function to complete the registration action of each function.
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {    
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}

// Definition of RegJNIRec
#ifdef NDEBUG
    #define REG_JNI(name)      { name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };
#else
    #define REG_JNI(name)      { name, #name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
        const char* mName;
    };
#endif

//Intercept the contents of some gRegJNI arrays
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_CharsetUtils),
        ............
}

        1.2.5 prepare the formal parameter argv of Java main function

        You are ready to enter the Java World and call the Java main function, but you need to pass the class name "com.android.internal.os.ZygoteInit" of the main function to be executed and a bunch of option s related to the virtual machine settings added earlier. Therefore, you need to prepare and pass it in the form of String [].

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ............
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");    //Find the Java String class first
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);    //Equivalent to strArray = new String[options.size() + 1];
    assert(strArray != NULL);
    //The string needs to be converted to the UTF format of the Java world, which is equivalent to classNameStr = new String("com.android.internal.os.ZygoteInit");
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);    //Equivalent to strArray[0] = classNameStr; i.e. "com.android.internal.os.ZygoteInit"
    
    //Convert all the options stored in the Vector to the String type of the Java World and store them in sequence in [1, options.size()] in strArray
    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }
    ............
}

        1.2.6 start the virtual machine

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ............
    //Convert "com.android.internal.os.ZygoteInit" to "com/android/internal/os/ZygoteInit" format
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);    //Find the ZygoteInit class according to the path
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");    //Find the static main method of the ZygoteInit class
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);    //Execute the ZygoteInit.main() function
#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    ............
}

        The action here is easy to understand. It is nothing more than finding com/android/internal/os/ZygoteInit.java, and then executing the main function of this class. Since then, it will enter the Java world. It should be noted that according to the comments in the code, this is the process of starting the virtual machine, and the current thread will become the main thread of the virtual machine, so this function can only exit when the virtual machine exits (such as crash) will return.

         1.2.7 release resources when exiting

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ............
    free(slashClassName);                            //Free up space occupied by string

    ALOGD("Shutting down VM\n");                     //Only when the virtual machine crashes or exits will it be executed here
    if (mJavaVM->DetachCurrentThread() != JNI_OK)    //Detach the current thread from the thread set visible to the virtual machine
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)               //Destroy the JavaVM you created earlier
        ALOGW("Warning: VM did not shut down cleanly\n");
}

        At this point, the code of the C/C + + part of the Zygote process will come to an end, and then it will enter the Java world. There are also many contents in the Java part that need to be explained. In view of the poor impression of the previous long blog posts, this blog post will be written here first. The Java part of Zygote will be placed in the next blog post Zygote - the fertilized egg of the Java World in Android system (II) Welcome To Java). The old rules, the last figure summarizes the calling process:

 

 

Posted by cuongvt on Fri, 15 Oct 2021 13:12:07 -0700