preface

For Android development, after a few years, you’ll either move up the ladder or switch industries. If you’re still working on Android, getting to know the Framework better is a must, so let’s get started on today’s topic.

As we know, for any programming language, the entry is usually the main function.

Where is the entry point for Android? How does his main function get started?

For those of you who are familiar with Android, there is an entry point for an application in ActivityThread, so the question is, when and by whom is main called in ActivityThread?

The above answers will be found in this article, if you are already familiar with the process, you may want to review them.

The Android framework

The Android platform is based on the Linux kernel. Android Runtime (ART/Dalvik) relies on the Linux kernel to perform low-level functions such as threading and low-level memory management.

On top of Android Runtime is the Java API Framework layer that we often contact. The following figure is the system architecture diagram of Android

Today’s topic is not a partial explanation of this architecture diagram, but rather an analysis of the Framework layer startup process closer to the application layer.

Summary of the Android Framework

If we leave out the Android architecture diagram here, what does the narrow framework k contain?

As I understand it, it can be divided into three parts: the server side, the client side, and the driver part that relies on the underlying capabilities of Linux.

The service side

ActivityManagerService (AMS), WindowManagerService (WMS), PackageM anerService (PMS)

  • AMS is primarily used to manage the activities of all applications
  • WMS manages various Windows, hiding, showing, etc
  • PMS is used to manage and track all application APK, installation, resolution, control permissions, etc.

There are also two classes for handling touch messages, KeyInputQueue and InputDispatchThread, one for reading messages and one for distributing them.

The client

It mainly includes ActivityThread, Activity, DecodeView and parent View, PhoneWindow, ViewRootImpl and internal class W, etc

  • ActivityThread is the client that communicates with AMS, and Activity is a familiar class for writing applications
  • After the introduction of several classes, you can refer to my other blog Android drawing principle analysis [dry goods]

Drivers that rely on Linux’s underlying capabilities

The main drivers are SurfaceFlingger (SF) and Binder

  • Each window corresponds to a Surface, and the function of SF driver is to display each Surface on the same screen
  • Binder kernel drivers provide IPC communication between servers and clients (or between servers).

Zygote

The first Dalvik VIRTUAL machine program that runs in the system is called Zygote, which means “one egg”, because all subsequent Dalvik virtual machine processes are hatched from this “egg”.

The Zygote process contains two main modules, as follows:

  • Socket server. The Socket server is used to receive commands to start the new Dalvik process.
  • The Framework shares classes and resources. When zygote starts, it loads shared classes defined in preload-classes and shared resources defined in preload-resources. The Zygote process is used to incubate other Dalvik processes. Therefore, after these classes and resources are loaded, the new Dalvik process does not need to load these classes and resources. This is called sharing.

The zygote process corresponds to the specific program app_rocess, which exists in the system/bin directory. The instructions to start the program are configured in init.rc.

Zygote has two excellent features ️

  • Each process fork is a Dalvik VIRTUAL machine. Independent process can prevent the crash of one program from causing the crash of all programs. This virtual machine is similar to Java virtual machine, and programmers can directly use Java to develop applications
  • The Zygote process preloads shared classes and shared resources, which are actually most of the classes and resources defined in the SDK. Therefore, when A new process is incubated through Zygote, the new APK process only needs to load the classes and resources contained in APK itself, which effectively solves the problem of multiple APKs sharing Framework resources.

SystemServer

The first Dalvik process hatched by Zygote is called SystemServer. SystemServer is just another name for the process, and the corresponding program is still app_process, because SystemServer was hatched from app_Process.

A Socket client is created in SystemServer, and AmS is responsible for managing the client, through which all Dalvik processes are indirectly started. When a new APK process needs to be started, AmS sends a startup command to the Socket server of zygote process through the Socket client, and Zygote incubates a new process. The server side, AMS, PMS, WMS, etc. mentioned above are all started in SystemServer.

The Android Framework source

The startup process of an operating system consists of three steps

  1. After powering on, load the bootloader program
  2. The operating system kernel is initialized
  3. Execute the first application

The Android operating system is based on the Linux Kernel. The Linux bootloader is used to initialize the operating system Kernel. At this time, it loads the init.rc file.

Init. Rc file

In the Android root directory, you can find init.rc directly

generic_x86:/ # ls
acct     bin        cache   config data         dev init            init.rc              init.usb.rc      lost+found mnt oem  product sdcard  sys    ueventd.rc 
adb_keys bugreports charger d      default.prop etc init.environ.rc init.usb.configfs.rc init.zygote32.rc metadata   odm proc sbin    storage system vendor     
Copy the code

Open the init. Rc

generic_x86:/ # cat init.rc
#... Omit the other
 # Now we can start zygote for devices with file based encryption
trigger zygote-start

# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start 
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on property:vold.decrypt=trigger_restart_framework
    stop surfaceflinger
# start SF,
    start surfaceflinger  
    
#... Omit other media, network, etc
Copy the code

That is, at init.rc, the Android kernel is launched.

app_process

The Android kernel also starts with the main method, which is in frameworks/base/ CMDS /app_process/app_main.cpp

int main(int argc, char* const argv[])
{...// Initialize AndroidRuntime
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    // Decide which service to start based on the parameters
    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") = =0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") = =0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") = =0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=".12) = =0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--".2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break; }}if(! className.isEmpty()) { args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);
    }else{.../ / start SystemServer
        if (startSystemServer) {
            args.add(String8("start-system-server")); }}...if (zygote) {
       / / start the Zygote
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        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."); }}Copy the code

So in this Main method, we initialize AppRuntime, whose parent is AndroidRuntime. Then we see that the Java class ZygoteInit is started.

How do you start a Java class in c++ code

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
   ...
   // Start the Java VIRTUAL machine
     if(startVm(&mJavaVM, &env, zygote, primary_zygote) ! =0) {
        return; }...// Find the Main method in Java
     jmethodID startMeth = env->GetStaticMethodID(startClass, "main"."([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
        // Execute the Main methodenv->CallStaticVoidMethod(startClass, startMeth, strArray); }... }Copy the code

As you can see, we created the Java virtual machine first and called the Java main method.

ZygoteInit

So what’s going on in the ZygoteInit class that starts Java? Let’s look at the key code, again starting with the Main method

//ZygoteInit.java
  public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    ZygoteHooks.startZygoteNoThreadCreation();
    
    / / create a socket
    zygoteServer.createZygoteSocket(socketName);
    Zygote.createBlastulaSocket(blastulaSocketName);
    
    if(! enableLazyPreload) { bootTimingsTraceLog.traceBegin("ZygotePreload");
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
            SystemClock.uptimeMillis());
            // Preloads system resources
            preload(bootTimingsTraceLog);
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
    } else {
        Zygote.resetNicePriority();
    }
    
    / / fork SystemServer process
    if (startSystemServer) {
        Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
        // child (system_server) process.
        if(r ! =null) {
            r.run();
            return; }}// Wait for Socket access
    if (caller == null) {
        Log.i(TAG, "Accepting command socket connections");
        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        caller = zygoteServer.runSelectLoop(abiList);
    }
Copy the code

The comments outline the initialization content of Zygote startup

  • Create a Socket
  • Loading System Resources
  • Start the SystemServer
  • RunSelectLoop waits for the socket to access and forks a new process.

SystemServer

Let’s continue with the SystemServer startup process, starting with the Main method

//SystemServer.java
   /** * The main entry point from zygote. */
    public static void main(String[] args) {
        new SystemServer().run();
    }
Copy the code

Very simple, create your own object and execute the run method, look at the run method

private void run(a) {...if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
        Slog.w(TAG, "System clock is before 1970; setting to 1970.");
        SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
    }
            
    VMRuntime.getRuntime().clearGrowthLimit();
    // The system server has to run all of the time, so it needs to be
    // as efficient as possible with its memory usage.
    VMRuntime.getRuntime().setTargetHeapUtilization(0.8 f);

    android.os.Process.setThreadPriority(
                android.os.Process.THREAD_PRIORITY_FOREGROUND);
    android.os.Process.setCanSelfBackground(false);
    Looper.prepareMainLooper();
    
   // Create the system service manager.
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    mSystemServiceManager.setStartInfo(mRuntimeRestart,
            mRuntimeStartElapsedTime, mRuntimeStartUptime);
    LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);

   try {
        traceBeginAndSlog("StartServices");
        startBootstrapServices();
        startCoreServices();
        startOtherServices();
        SystemServerInitThreadPool.shutdown();
    } catch (Throwable ex) {
           Slog.e("System"."* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");
            Slog.e("System"."************ Failure starting system services", ex);
            throw ex;
    } finally {
        traceEnd();
    }
    
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
Copy the code

The run method is mainly used to set the phone time, set the virtual machine memory size, create a message loop Looper, create SystemServiceManager, etc., the most important is to start various services, we then look at startBootstrapServices, StartCoreServices, startOtherServices method

private void startBootstrapServices(a) { Installer installer = mSystemServiceManager.startService(Installer.class); mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class); mActivityManagerService = mSystemServiceManager.startService( ActivityManagerService.Lifecycle.class).getService(); mActivityManagerService.setSystemServiceManager(mSystemServiceManager); mActivityManagerService.setInstaller(installer); . }private void startCoreServices(a) {... mSystemServiceManager.startService(BatteryService.class); . }private void startOtherServices(a) {
        final Context context = mSystemContext;
        VibratorService vibrator = null;
        DynamicAndroidService dynamicAndroid = null;
        IStorageManager storageManager = null;
        NetworkManagementService networkManagement = null;
        IpSecService ipSecService = null;
        NetworkStatsService networkStats = null;
        NetworkPolicyManagerService networkPolicy = null;
        ConnectivityService connectivity = null;
        NsdService serviceDiscovery= null;
        WindowManagerService wm = null;
        SerialService serial = null;
        NetworkTimeUpdateService networkTimeUpdater = null;
        InputManagerService inputManager = null;
        TelephonyRegistry telephonyRegistry = null;
        ConsumerIrService consumerIr = null;
        MmsServiceBroker mmsService = null;
        HardwarePropertiesManagerService hardwarePropertiesService = null; . }Copy the code

In these methods we start our core services and our common services. AMS, PMS, WMS and other related services can also be seen in the code.

Introduction to Launcher

Once a service is started, it will want to call the systemReady() method. See a listening callback in SysytemServer startOtherServices

mActivityManagerService.systemReady(() -> {
  try {
        startSystemUi(context, windowManagerF);
    } catch (Throwable e) {
        reportWtf("starting System UI", e); }}}static final void startSystemUi(Context context, WindowManagerService windowManager) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui"."com.android.systemui.SystemUIService"));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        //Slog.d(TAG, "Starting service: " + intent);
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        windowManager.onSystemUiStarted();
}
Copy the code

When Ams starts, it displays the Launcher

//AMS.java
 public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {... startHomeActivityLocked(currentUserId,"systemReady");
     //mStackSupervisor.resumeFocusedStackTopActivityLocked(); . }Copy the code

When the ActivityThread main method is called has yet to be answered.

How is the main method of ActivityThread called?

AMS is used to manage the start and end of an Activity, etc. Check the AMS code, when the currently started APP does not create a process, it will eventually call ZygoteProcess, and then send a socket request to Zygote.

resumeTopActivityLocked -> startProcessLocked -> Process.start() -> ZygoteProcess.start() -> ZygoteProcess. StartViaZygote () – > ZygoteProcess. ZygoteSendArgsAndGetResult ()

Next, take a look at the rough code

//AMS.java
private final boolean startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
            // entryPoint is ActivityThread
     final String entryPoint = "android.app.ActivityThread";

    return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
                    startTime);    
}
Copy the code
//ZygoteProcess.java
private Process.ProcessStartResult startViaZygote(final String processClass,... ){
    ArrayList<String> argsForZygote = new ArrayList<String>();
    argsForZygote.add("--runtime-args");
    argsForZygote.add("--setuid=" + uid);
    argsForZygote.add("--setgid=" + gid);
    argsForZygote.add("--runtime-flags=" + runtimeFlags);
    argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
    if (startChildZygote) {
        argsForZygote.add("--start-child-zygote");
    }
    argsForZygote.add(processClass);
    synchronized(mLock) {
        returnzygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), useBlastulaPool, argsForZygote); }}// Initiate a Socket request to Zygote
private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ArrayList
       
         args,..)
       {
    blastulaSessionSocket = zygoteState.getBlastulaSessionSocket();

    final BufferedWriter blastulaWriter = new BufferedWriter(
            new OutputStreamWriter(blastulaSessionSocket.getOutputStream()),
                                Zygote.SOCKET_BUFFER_SIZE);
    final DataInputStream blastulaReader =
    newDataInputStream(blastulaSessionSocket.getInputStream()); blastulaWriter.write(msgStr); blastulaWriter.flush(); . }Copy the code

Eventually Zygote gets a request to call the ZygoteInit method in ZygoteInit

//ZygoteInit.java
 public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {
        if (RuntimeInit.DEBUG) {
            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();

        RuntimeInit.commonInit();
        ZygoteInit.nativeZygoteInit();
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }
Copy the code

FindStaticMain in RuntimeInit, and finally execute the Runnable returned in ZygoteInit to call main.

protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {
        nativeSetExitWithoutCleanup(true);
        VMRuntime.getRuntime().setTargetHeapUtilization(0.75 f);
        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
        final Arguments args = new Arguments(argv);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }

protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) {.. m = cl.getMethod("main".new Class[] { String[].class });
        return new MethodAndArgsCaller(m, argv);
    }
Copy the code

conclusion

If you saw this and wanted to give it a thumbs up, thank you. Because my level is limited, each big guy if see the article has a mistake, welcome to point out, express thanks here.

Recommended reading

  • Android related knowledge points personal summary
  • Android “draw” beauty makeup, photoshop (change id photo background, stain repair, etc.)

Reference books

“Android kernel anatomy” Ke Yuandan