Slow down ~ sort out the Activity startup process again

preface

The process of starting an activity is divided into two parts: one is to start an activity through the startactivity (intent) method in the activity; Second, we start an App by clicking the application icon on the desktop, and then display the activity; The second method is more comprehensive than the first method, so this paper will analyze it with the second process.

briefly

The desktop of our mobile phone is an Activity called Launcher, which lists the application icons in the mobile phone. The icon contains information such as the default startup page of the application parsed when installing apk. When clicking the application icon, the App to be started is different from the processes of Launcher, AMS and Zygote. Therefore, an App will be started only after multiple communication between Launcher and AMS, AMS and Zygote, and AMS and new App. Then the Activity will be started. The overall sequence diagram is as follows:

I Launcher sends start Activity to AMS

When the user clicks the application icon, Launcher Activity calls the startActivitySafely method and finally calls Activity.. Startactivity method. The method calling process is as follows:

Launcher.java
   public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
       ...
       //Mark start on new stack
       intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
       ...
       startActivity(intent, optsBundle);
       ...
   }

Activity.java


   @Override
   public void startActivity(Intent intent, @Nullable Bundle options) {
       ...
       if (options != null) {
           //-1 is requestCode, indicating that you do not need to know whether the startup is successful
           startActivityForResult(intent, -1, options);
       } else {
           startActivityForResult(intent, -1);
       }
   }

   public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
           @Nullable Bundle options) {
           ...
           Instrumentation.ActivityResult ar =
               mInstrumentation.execStartActivity(
                   this, mMainThread.getApplicationThread(), mToken,this,intent, requestCode, options);
           ...
    }

Each Activity holds an Instrumentation object. It continues to complete the process of starting the Activity through its execStartActivity method. Note that mmainthread is passed in this method Getapplicationthread(), which obtains the ApplicationThread instance of the internal class of ActivityThread, which is a Binder object. AMS then communicates with App through this object.

Instrumentation.java
public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
    ...
	int result = ActivityTaskManager.getService().startActivity(whoThread,who.getBasePackageName(),
        who.getAttributionTag(),intent,intent.resolveTypeIfNeeded(who.getContentResolver()),
        token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); 
    ...
}

ActivityTaskManager.java
public static IActivityTaskManager getService() {
       return IActivityTaskManagerSingleton.get();
}
    
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    return IActivityTaskManager.Stub.asInterface(b);
                }
             }
};

In this step, the Launcher starts to communicate with AMS. Since it is in different processes, it needs to communicate through Binder. IActivityTaskManager is an object representing Binder on AMS side, and AMS starts startActivity.

Here, the Launcher requests AMS to start an Activity.

II AMS starts the Activity and notifies the Launcher to enter the Paused state

In the next process, we switch to the process where AMS is located to continue the analysis. In the previous step, we called the startActivity method of AMS through the agent. The next calls are as follows:

ActivityTaskManagerService.java
    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
            Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }
    
    @Override
    public int startActivityAsUser(IApplicationThread caller, String callingPackage,
            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
            Bundle bOptions, int userId) {
        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
                true /*validateIncomingUser*/);
    }
    
    private int startActivityAsUser(IApplicationThread caller, String callingPackage,
            @Nullable String callingFeatureId, Intent intent, String resolvedType,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
	    ...
        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
                
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setCallingFeatureId(callingFeatureId)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setUserId(userId)
                .execute();
    }
    
    ActivityStarter obtainStarter(Intent intent, String reason) {
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }

The above steps are mainly for permission check

ActivityStarter.java
 int execute() {
 ...
 res = executeRequest(mRequest);
 ...
 }
//The following method will be called layer by layer
ActivityStack.java
 private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    ...
    if (mResumedActivity != null) {
       pausing |= startPausingLocked(userLeaving, false , next);
    }
    ...
    mStackSupervisor.startSpecificActivity(next, true, false);
    ...
 }

The startPausingLocked method is mainly used to notify the Launcher to enter the Paused state. After it enters this state, it can pass the activitystacksupervisor The startspecificactivity method judges the status of the process to which the Activity to start belongs and makes different responses.

ActivityStackSupervisor.java
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // Get the Activity process information to start
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
        boolean knownToBeDead = false;
        //If a process exists and there are threads in the process, an Activity of the same application is started (ordinary activities are executed here)
        if (wpc != null && wpc.hasThread()) {
            try {
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
            knownToBeDead = true;
        }
	//Otherwise, request the Zygote process to create a new process through AMS
        r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
        final boolean isTop = andResume && r.isTopRunningActivity();
        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}

Up to now, we have completed the communication between the Launcher and AMS, as well as the communication between AMS and Zygote process. Next, we will create the thread of the App to start, that is, ActivityThread.

III The new process starts, and the main function entry of ActivityThread

At the end of the previous section, when Zygote starts a new process, it marks activitythread Main function. After Zygote creates a new process, this method is called through reflection. The subsequent process is in a new App process. Of course, it will still communicate with AMS.

ActivityThread.java
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
	...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
	...
        Looper.loop();
	...
    }
    
    private void attach(boolean system, long startSeq) {
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ...
    }
ActivityManagerService.java
    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
            ...
            thread.bindApplication(processName, appInfo, providerList,
                        instr2.mClass,
                        profilerInfo, instr2.mArguments,
                        instr2.mWatcher,
                        instr2.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.compat, getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions,
                        app.mDisabledCompatChanges);
             ...
             didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
             ...
    }

Here, the Looper and ActivityThread objects are created, and then the current application ApplicationThread is registered in AMS. ApplicationThread is the internal class of ActivityThread and implements iaapplicationthread Stub uses this object to communicate across processes.

The above code logic is divided into two steps:

In the first step, when AMS binds ApplicationThread, it sends an H.BIND_APPLICATION Message. The onCreate method of application is called when processing the Message in the Handler.

The second step is to call the activitystacksupervisor layer by layer in the attachApplication of mAtmInternal The realstartactivitylocked method enters the preparation Activity transaction phase.

The overall is as follows:

public final void bindApplication(String processName, ApplicationInfo appInfo,
                ProviderInfoList providerList, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, AutofillOptions autofillOptions,
                ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
                	...
                	sendMessage(H.BIND_APPLICATION, data);
 }
                
public void handleMessage(Message msg) {
    switch (msg.what) {
       case BIND_APPLICATION:
       	AppBindData data = (AppBindData)msg.obj;
       	handleBindApplication(data);
       	Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
       	break;
        ...
      }
}

private void handleBindApplication(AppBindData data) {
	...
	mInstrumentation.callApplicationOnCreate(app);
	...
}

So far, the new App thread has been started and bound to the Application.

IV Create Activity

ActivityStackSupervisor.java
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
       boolean andResume, boolean checkConfig) throws RemoteException {
       ...
       final ClientTransaction clientTransaction = ClientTransaction.obtain(
                proc.getThread(), r.appToken);
         final DisplayContent dc = r.getDisplay().mDisplayContent;
         //LaunchActivityItem is added to the lifecycle here
         clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                 System.identityHashCode(r), r.info,
                 mergedConfiguration.getGlobalConfiguration(),
                 mergedConfiguration.getOverrideConfiguration(), r.compat,
                 r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                 r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
                 dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                 r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));

         final ActivityLifecycleItem lifecycleItem;
         if (andResume) {
             //ResumeActivityItem is added here
             lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
         } else {
             lifecycleItem = PauseActivityItem.obtain();
         }
         clientTransaction.setLifecycleStateRequest(lifecycleItem);
         //Execute clientTransaction
         mService.getLifecycleManager().scheduleTransaction(clientTransaction);
         ...
}

The ClientTransaction manages the startup information of the Activity. The LaunchActivityItem inherits the activitylifecycle item, visualizes the life cycle of the Activity, and can be executed by the clientlifecycle manager. Next, execute is sent in the scheduleTransaction method_ The transaction message is processed by class H of ActivityThread, and then the transactionexecutor Execute(), and finally execute the handleLaunchActivity method, as follows

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        ...
    }

    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

    class H extends Handler {
         ...
         public void handleMessage(Message msg) {
         ...
            case EXECUTE_TRANSACTION:
                final ClientTransaction transaction = (ClientTransaction) msg.obj;
                mTransactionExecutor.execute(transaction);
                if (isSystem()) {
                    transaction.recycle();
                }
                break;
         ...
         }  
         ...
    }

    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        ...
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
    }

Next, the ActivityThread handles the subsequent operations

public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        ...
        final Activity a = performLaunchActivity(r, customIntent);
        ...
        return a;
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ContextImpl appContext = createBaseContextForActivity(r);
        ...
        //Create reflection Activity
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        ...
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        ...
        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);
        ...
        activity.setTheme(theme);
        ...
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
}

The performLaunchActivity method mainly does the following:

  1. Create the context in which you want to start the activity
  2. Create an activity instance in reflection form through the newActivity method of Instrumentation
  3. If the Application does not exist, it will create the Application and call the onCreate method of the Application
  4. Initialize the Activity, create a Window object (PhoneWindow) and associate the Activity with the Window
  5. Call the onCreate method of the Activity through Instrumentation

summary

It is of great significance to start the process based on the Activity as a whole. It is suggested to start with the overall process, and then deliberate on the details. When the process is blocked, you can also debug. Overall startup process of Activity:

  1. Click the icon and the Launcher requests AMS to start the App
  2. AMS feedback received the startup request and informed the Launcher to enter the pause state
  3. Launcher enters Paused status and informs AMS
  4. AMS detects whether the new App has been started. Otherwise, it notifies Zygote to create a new process and call activitythread main method
  5. The application process starts the ActivityThread
  6. Class H in ActivityThread handles the request message that needs to start the Activity

last

If this article provides effective information, please give me a little praise. If there is any discrepancy, you can comment or send me a private letter. Thank you.

Video: . Detailed explanation and handwritten implementation of Activity startup process

Original text: https://juejin.cn/post/6940155030578135048

Keywords: Android

Added by Retired Bill on Mon, 13 Dec 2021 16:52:53 +0200