Android interview advanced (XIII) – App startup process

Time:2022-1-4
Q: briefly describe the app startup process and the activity startup process

A: the app starts when the user clicks the icon on the desktop. The Android desktop application is called launcher. The desktop can also be regarded as an application of the Android system. The desktop is only one page of the application. Each application in Android is a process. If an application starts another application and communicates, there will be inter process communication, that is, IPC communication. Most of the IPC in Android is realized through binder. In this way, the app startup process is actually the activity startup process, but it will judge whether the process where the activity is to be started exists. If it does not exist, first notify the zygote process fork() of a new process, and then start the target page.If you want to just look at the summary, turn to the back and summarize seven points:
1. From the launcheractivity of the desktop application, click Start:

//Launcheractivity inherits the child listactivity, which is actually the desktop used to store many applications
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Intent intent = intentForPosition(position);
        startActivity(intent);    // Execute startactivity to activity
    }

    //Then look at the startactivity of the activity -- the startactivityforresult is executed again
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        If (mparent = = null) {// mparent is used to judge whether there is a parent activity. It is basically not in nested activities, and else will not read it
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation. Execstartactivity (// mmainthread) is the instance of activitythread and the class of the main method of the application entry.
                //The application has not been started at this time. Where are the instances from? In fact, this is the launcher's, that is, the current one.
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            //....
        } else {
           //....
        }
    }

The above code is activity The startactivityforresult method calls execstartactivity through the mimstrumentation object, which is mainly used to monitor the interaction between the application and the system. Mmainthread is actually an activitythread object, which is actually the activitythread of the launcher application. It is initialized when the launcher is started.

//Instrumentation. Java class 
  public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target,
        Intent intent, int requestCode, Bundle options) {
        //....
        try {
            //....
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getBasePackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
                    requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
     
    //ActivityTaskManager. Getservice () to see what is obtained:
    /**  ActivityTaskManager.java*/
    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    //Get a service through servicemanager. No matter what service is here, servicemanager is a class that manages all services in the system
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    //What kind of service do you get? See XXX here Stub. Asinterface(), isn't this something you often see in Aidl? Let's see who implements iactivitytaskmanager Stub interface
                    return IActivityTaskManager.Stub.asInterface(b);
                }
            };
    //Here it is:
    public class ActivityTaskManagerService extends IActivityTaskManager.Stub

The activity task manager service is called ATMs for short. Here we understand: the launcher starts an app through a series of operations,Using Aidl through binderThe data is handed over to ATMs (here is an IPC communication to start the application, i.e. a communication between the launcher and ATMs). ATMs starts to process the message. Instrumentation has passed the process where the launcher is located (the whothread parameter, which is the previous to mmainthread. Getapplicationthread). AMS saves it as an activityrecord object, There is an applicationthreadproxy in this object, that is, binder’s proxy object. AMS sends a message to the launcher through applicationtreadproxy. After receiving the message, ATMs will notify the previous page that it is time to rest, that is, to enter the pause state. Here, ATMs will notify the launcher that it is time to rest:

//ActivityStack.java
  private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        //...
        ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
        //...
        boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, next, false);
        }
        //...

        if (next.attachedToProcess()) {
            //App started
            try {
                //...
                transaction.setLifecycleStateRequest(
                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                getDisplay().mDisplayContent.isNextTransitionForward()));
                mService.getLifecycleManager().scheduleTransaction(transaction);
                //...
            } catch (Exception e) {
                //...
                mStackSupervisor.startSpecificActivityLocked(next, true, false);
                return true;
            }
            //...
            // From this point on, if something goes wrong there is no way
            // to recover the activity.
            try {
                next.completeResumeLocked();
            } catch (Exception e) {
                // If any exception gets thrown, toss away this
                // activity and try the next one.
                Slog.w(TAG, "Exception thrown during resume of " + next, e);
                requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
                        "resume-exception", true);
                return true;
            }
        } else {
            //Cold start process
            mStackSupervisor.startSpecificActivity(next, true, true);
        }
    }

First, startpausinglocked () puts the previous page in the pause state,Then start to judge whether the process of the app to be started exists, start the target activity directly if it exists, and create a process if it does not exist. If it is currently a launcher, there is a process. EnterHot start process, does not exist, enterCold start process。 It is also why the target activity starts to execute the lifecycle method after onpause is executed when an activity is started in the activity lifecycle. Then look at the cold start process:

//ActivityStackSupervisor.java  
  void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);

        boolean knownToBeDead = false;
        if (wpc != null && wpc.hasThread()) {
            try {
                //Continue to determine whether there is a process. If there is a process, it returns 
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
             
            }
            knownToBeDead = true;
        }
        //....   Mservice is an instance object of ATMs. Here we create a process. Let's see how to create a process
        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
    }

How does ATMs create a new process? In other words, how to communicate with zygote? Let the zygote process fork() process. Look at ATMs:

//ActivityTaskManagerService.java
  void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
            String hostingType) {
        try {
            //...
            // Post message to start process to avoid possible deadlock of calling into AMS with the
            //This means: publish a message to start the process to avoid possible deadlock when calling AMS
            // ATMS lock held.
            final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
                    mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
                    isTop, hostingType, activity.intent.getComponent());
            mH.sendMessage(m);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

Activitymanagerinternal:: startprocess look at this and start a new process:

//Is an abstract method. Find the place to implement it:
public abstract void startProcess(String processName, ApplicationInfo info,
            boolean knownToBeDead, boolean isTop, String hostingType, ComponentName hostingName);
//The class that implements the activitymanagerinternal:: startprocess method is in activitymanagerservice:

//ActivityManagerService.java
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        // ... ...
    public final class LocalService extends ActivityManagerInternal {
        @Override
        public void startProcess(String processName, ApplicationInfo info,
                boolean knownToBeDead, String hostingType, ComponentName hostingName) {
            try {
                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
                            + processName);
                }
                //Thread synchronization prevents multithreading from creating processes. Process creation can only support single thread. Therefore, binder cannot be used for subsequent communication between AMS and zygote. Socket is selected 
                synchronized (ActivityManagerService.this) {
                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                            new HostingRecord(hostingType, hostingName),
                            false /* allowWhileBooting */, false /* isolated */,
                            true /* keepIfLarge */);
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }
    }
    // ... ...
}

Here you can see that in the cold start process, AMS is entrusted with the task of creating a new process, which is finally executed in the zygote process, which is responsible for creating a new process.

//ZygoteProcess.java
    private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
            ZygoteState zygoteState, String msgStr)
            throws ZygoteStartFailedEx, IOException {
        try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
            final BufferedWriter usapWriter =
                    new BufferedWriter(
                            new OutputStreamWriter(usapSessionSocket.getOutputStream()),
                            Zygote.SOCKET_BUFFER_SIZE);
            final DataInputStream usapReader =
                    new DataInputStream(usapSessionSocket.getInputStream());

            usapWriter.write(msgStr);
            usapWriter.flush();

            Process.ProcessStartResult result = new Process.ProcessStartResult();
            result.pid = usapReader.readInt();
            // USAPs can't be used to spawn processes that need wrappers.
            result.usingWrapper = false;

            if (result.pid >= 0) {
                return result;
            } else {
                throw new ZygoteStartFailedEx("USAP specialization failed");
            }
        }
    }

It can be seen that the communication here is actually through socket and zygote, and bufferedwriter is used to read and receive messages. Here, the message of the new process is passed to zygote. Zygote forks the process and returns the PID of the new process. In this process, the activitythread object is also instantiated, and then the main method is executed:

//RuntimeInit.java
   protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;

        try {
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    "Missing class when invoking static main " + className,
                    ex);
        }

        Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }
        //...
        return new MethodAndArgsCaller(m, argv);
    }

Here, the main method is called through reflection, and then enter the main method of the main entry of app.

public static void main(String[] args) {
        //...
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        //Here will tell AMS that the process is created and I'm ready to start Create application at the same time
        thread.attach(false, startSeq);

        //...

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        //...
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Here, AMS tells zygote that creating a new process is almost complete,A cigarette afterwards is better than a living immortal, I’ll take a break
Continue now:
After the process is created, the application is actually started, because an application is actually a process. At this time, the application starts to create the application and start the activity itself, and then restart the startactivity to the beginning. At this time, if the process of the target activity already exists, it enters the activity startup process, Finally execute the handlelaunchactivity() method to the activitythread:

//ActivityThread.java
public Activity handleLaunchActivity(ActivityClientRecord r,
                                         PendingTransactionActions pendingActions, Intent customIntent) {
        //...
        WindowManagerGlobal.initialize();
        //...  Then execute performlaunchactivity
        final Activity a = performLaunchActivity(r, customIntent);
        //...
        return a;
    }

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //Create contextimpl
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //Create activity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        }

        try {
            if (activity != null) {
                //Complete the initialization of some important data of activity
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }

                //Set the theme of the activity
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                //Call the oncreate method of activity. At this point, it is almost over
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
            }
        }
        return activity;
    }

Then here, the second game is over. To sum up the achievements of this time, I don’t know how much energy was consumed and exhausted, just for the last moment, that is:Attention, focus here, HKCEE!
1. The launcher is called to click the event and go to the startactivity method of the instrumentation class.
2. Instrumentation uses binder mechanism through Aidl to tell ATMs the requirements to start the application.
3. After ATMs receives the request, it will feed back to the launcher and let the launcher enter the paid state
4. The launcher enters the paused state. ATMs gives AMS the task of creating a process. AMS communicates with zygote through socket and tells zygote that a new process needs to be created.
5. Zygote fork process and call the main method of activitythread, that is, the entry of app.
6. The main method of activitythread creates a new activitythread instance and a new looper instance to start the loop loop loop.
At the same time, activitythread also tells AMS that after the process is created, it starts to create application and provider, and calls applicaiton’s attach and oncreate methods.
7. Finally, create the context, load the activity through the class loader, and call the oncreate method of the activity.

Finally: in fact, understanding the startup process is also the final preparation for startup optimization. In this way, what startup optimization can do is the last:
1. Application’s attach method, in which multidex logic is executed. Therefore, multidex can be optimized here. For example, today’s headline scheme is to start the activity of a process separately to load multidex.
2. The oncreate method of application and the initialization of a large number of third-party libraries are carried out here, so we can start the thread pool, lazy loading, etc. Distinguish each startup task, which can be run by sub threads and which have priority.
3. The oncreate method of activity also performs thread processing and lazy loading. Or pre create activities, load classes in advance, and so on.

When I see the two pictures, sometimes I eat my own home, which may not taste good. I can also steal some, so I stole the two pictures:

Android interview advanced (XIII) - App startup process

image.png
Android interview advanced (XIII) - App startup process

image.png

Recommended Today

JVM + GC parsing (premise knowledge concatenation)

Premise preparation JVM GC garbage collection JVM virtual machine monitoring, tuning and troubleshooting Tomcat and microservice optimization 1. Premise review 1.1. JVM memory structure 1.1.1、 JVM Architecture Overview The gray part in the figure isThread private, there is almost no garbage collectionOrange partThread sharing, the main place where garbage recycling occurs What is the class […]