Preface
This is Android 9.0 AOSP series Let's review the general contents of the previous articles.
This paper mainly introduces the starting process of Zygote, the first Java process in Android world.
- Register server socket to respond to client requests
- Various preloading operations, classes, resources, shared libraries, etc
- Force GC once
- fork SystemServer process
- Loop to wait for socket request from client (request socket connection and request fork application process)
This paper mainly introduces the system server, the first process of the Zygote process fork, which hosts the creation and startup of various system services.
Language, time zone, region and other settings
Virtual machine memory settings
Fingerprint information, Binder call settings
Looper.prepareMainLooper(), create the main thread looper
Initialize the native service and load libandroid_servers.so
createSystemContext(), initializing the system context
Create system service manager
startBootstrapServices, start system boot service
Start core services
Startother services, start other services
Looper.loop() to open the message loop
At the end of startOtherServices, the onSystemReady() method of AMS will be called to start the desktop Activity.
This paper mainly introduces the process of AMS requesting Zygote to create application process, that is, socket communication to Zygote process, which corresponds to the first part.
Call Process.start() to create application process
ZygoteProcess is responsible for establishing socket connection with Zygote process, and sending the parameters required for creating process to Zygote's socket server
After the Zygote server receives the parameters, it calls ZygoteConnection.processOneCommand() to process the parameters, and the fork process.
Finally, find and execute the main() method of ActivityThread class through findStaticMain(), and the subprocess will start
This paper mainly introduces the starting process of activity manager service (AMS), which is closely related to the starting, switching, scheduling and application process management of the four components.
AMS initialization, via the constructor of ActivityManagerService.Lifecycle
setSystemProcess(), register various services, create ProcessRecord, and update the value of oom_adj
Install system Provider
systemReady() will eventually start the desktop Home Activity
Today, I want to introduce the startup process of Activity. The launch of Activity is a big project with a lot of details. This article will simply sort out the whole startup process, and will not go too deep into the source code details. The key issues, such as the handling of launchMode and life cycle, will be further analyzed in a separate article.
Start process analysis
First come to a flow chart, which is more convenient to understand.
Then, in the previous analysis, the systemReady() method of ActivityManagerService will finally start the desktop Hme Activity, and the method called is startHomeActivityLocked.
> ActivityManagerService.java boolean startHomeActivityLocked(int userId, String reason) { ...... Intent intent = getHomeIntent(); ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId); if (aInfo != null) { ...... if (app == null || app.instr == null) { intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK); final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid); final String myReason = reason + ":" + userId + ":" + resolvedUserId; // Start desktop Activity mActivityStartController.startHomeActivity(intent, aInfo, myReason); } } else { Slog.wtf(TAG, "No home screen found for " + intent, new Throwable()); } return true; }
Call startHomeActivity() method of ActivityStartController:
> ActivityStartController.java void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) { mSupervisor.moveHomeStackTaskToTop(reason); mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason) .setOutActivity(tmpOutRecord) .setCallingUid(0) .setActivityInfo(aInfo) .execute(); mLastHomeActivityStartRecord = tmpOutRecord[0]; if (mSupervisor.inResumeTopActivity) { mSupervisor.scheduleResumeTopActivities(); } }
The obtainStarter() method returns the ActivityStarter object, which is responsible for the startup of Activity. A series of setXXX() methods pass in various parameters required for startup. The final execute() is the real startup logic.
Before you continue to look at the source code, think about which process you are in now? AMS is initialized in the system server process, so the above work is all in the system server process. The startActivity() method we usually use in the development process is obviously called in the application process. So, what kind of call chain is the normal startActivity() method? Follow up the Activity.startActivity() method to see.
> Activity.java @Override public void startActivity(Intent intent, @Nullable Bundle options) { if (options != null) { startActivityForResult(intent, -1, options); } else { startActivityForResult(intent, -1); } } public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { options = transferSpringboardActivityOptions(options); // Call the Instrumentation.execStartActivity() method Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); if (ar != null) { // Callback ActivityResult mMainThread.sendActivityResult( mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } if (requestCode >= 0) { mStartedActivity = true; } cancelInputsAndStartExitTransition(options); } else { // Finally, it calls the Instrumentation.execStartActivity() method if (options != null) { mParent.startActivityFromChild(this, intent, requestCode, options); } else { mParent.startActivityFromChild(this, intent, requestCode); } } }
Finally, the execStartActivity() method of instrumentation will be called. Instrumentation is a very important class, which is indispensable for the start of Activity and the callback of life cycle. This class will be encountered many times later.
> Instrumentation.java public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; ...... try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); // Binder calls AMS to start Activity int result = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); // Test startup results checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } return null; }
Here, the startActivity() method of AMS is called through Binder. ActivityManager.getService() doesn't need to think about getting AMS proxy object.
> ActivityManager.java public static IActivityManager getService() { return IActivityManagerSingleton.get(); } private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() { @Override protected IActivityManager create() { final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; } };
Then go to AMS's startActivity() method.
> ActivityManagerService.java @Override public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {setMayWait return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId()); } public final int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { enforceNotIsolatedCaller("startActivity"); userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser"); // TODO: Switch to user app stacks here. return mActivityStartController.obtainStarter(intent, "startActivityAsUser") // Get ActivityStarter object .setCaller(caller) .setCallingPackage(callingPackage) .setResolvedType(resolvedType) .setResultTo(resultTo) .setResultWho(resultWho) .setRequestCode(requestCode) .setStartFlags(startFlags) .setProfilerInfo(profilerInfo) .setActivityOptions(bOptions) .setMayWait(userId) .execute(); }
Next, it is similar to the previous start of Home Activity. Get the ActivityStarter object, provide parameters, and finally execute().
obtainStarter() gets the ActivityStarter object through factory mode.
ActivityStarter obtainStarter(Intent intent, String reason) { return mFactory.obtain().setIntent(intent).setReason(reason); }
The default implementation of mFactory is ActivityStarter.DefaultFactory.
> ActivityStarter.java static class DefaultFactory implements Factory { /** * The maximum count of starters that should be active at one time: * 1. last ran starter (for logging and post activity processing) * 2. current running starter * 3. starter from re-entry in (2) * * A maximum of three starter s can be activated at the same time. */ private final int MAX_STARTER_COUNT = 3; private ActivityStartController mController; private ActivityManagerService mService; private ActivityStackSupervisor mSupervisor; private ActivityStartInterceptor mInterceptor; private SynchronizedPool<ActivityStarter> mStarterPool = new SynchronizedPool<>(MAX_STARTER_COUNT); DefaultFactory(ActivityManagerService service, ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) { mService = service; mSupervisor = supervisor; mInterceptor = interceptor; } @Override public void setController(ActivityStartController controller) { mController = controller; } @Override public ActivityStarter obtain() { // Get from synchronized pool ActivityStarter starter = mStarterPool.acquire(); if (starter == null) { starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor); } return starter; } @Override public void recycle(ActivityStarter starter) { starter.reset(true /* clearRequest*/); mStarterPool.release(starter); } }
A synchronous object cache pool with a capacity of 3 is provided to cache ActivityStarter objects. The setXXX() method is parameter configuration. Note that the setMayWait method will set the mayWait parameter to true. Let's look directly at its actual execution, the execute() function.
> ActivityStarter.java int execute() { try { if (mRequest.mayWait) { // Set mayWait() method to true return startActivityMayWait(mRequest.caller, mRequest.callingUid, mRequest.callingPackage, mRequest.intent, mRequest.resolvedType, mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.startFlags, mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig, mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId, mRequest.inTask, mRequest.reason, mRequest.allowPendingRemoteAnimationRegistryLookup, mRequest.originatingPendingIntent); } else { return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo, mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.callingPid, mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.componentSpecified, mRequest.outActivity, mRequest.inTask, mRequest.reason, mRequest.allowPendingRemoteAnimationRegistryLookup, mRequest.originatingPendingIntent); } } finally { // Recycle the current ActivityStarter object onExecutionComplete(); } }
Then call startActivityMayWait().
> ActivityStarter.java private int startActivityMayWait(IApplicationThread caller, int callingUid, String callingPackage, Intent intent, String resolvedType, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, WaitResult outResult, Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity, int userId, TaskRecord inTask, String reason, boolean allowPendingRemoteAnimationRegistryLookup, PendingIntentRecord originatingPendingIntent) { ..... // Save a copy in case ephemeral needs it final Intent ephemeralIntent = new Intent(intent); // Don't modify the client's object! // Recreate without modifying the client's original intent intent = new Intent(intent); if (componentSpecified && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null) && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction()) && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction()) && mService.getPackageManagerInternalLocked() .isInstantAppInstallerComponent(intent.getComponent())) { intent.setComponent(null /*component*/); componentSpecified = false; } // Get ResolveInfo ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0 /* matchFlags */, computeResolveFilterUid( callingUid, realCallingUid, mRequest.filterCallingUid)); ...... // Collect information about the target of the Intent. // Get ActivityInfo of target Intent ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo); synchronized (mService) { final ActivityStack stack = mSupervisor.mFocusedStack; stack.mConfigWillChange = globalConfig != null && mService.getGlobalConfiguration().diff(globalConfig) != 0; ...... final ActivityRecord[] outRecord = new ActivityRecord[1]; // Call startActivity() method int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent); Binder.restoreCallingIdentity(origId); ...... if (outResult != null) { // Set startup results outResult.result = res; final ActivityRecord r = outRecord[0]; switch(res) { case START_SUCCESS: { mSupervisor.mWaitingActivityLaunched.add(outResult); do { try { // Wait for start result mService.wait(); } catch (InterruptedException e) { } } while (outResult.result != START_TASK_TO_FRONT && !outResult.timeout && outResult.who == null); if (outResult.result == START_TASK_TO_FRONT) { res = START_TASK_TO_FRONT; } break; } ...... break; } } } return res; } }
Call the startActivity() method to start the Activity, which has two overloaded methods called in turn. Wait for the startup result here.
> ActivityStarter.java private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup, PendingIntentRecord originatingPendingIntent) { int err = ActivityManager.START_SUCCESS; ProcessRecord callerApp = null; if (caller != null) { // When the caller is not empty, use AMS to find ProcessRecord callerApp = mService.getRecordForAppLocked(caller); if (callerApp != null) { callingPid = callerApp.pid; callingUid = callerApp.info.uid; } else { err = ActivityManager.START_PERMISSION_DENIED; } } // sourceRecord is used to describe the Activity that initiates this request // resultRecord user description Activity receiving startup result // Generally, the two activities should be the same ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; ...... // Get start flag final int launchFlags = intent.getFlags(); ...... if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) { // No class could be found to handle the intent err = ActivityManager.START_INTENT_NOT_RESOLVED; } if (err == ActivityManager.START_SUCCESS && aInfo == null) { // The Activity class specified in intent was not found err = ActivityManager.START_CLASS_NOT_FOUND; } ...... // Permission check boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultStack); abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); ...... // Build ActivityRecord ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null, mSupervisor, checkedOptions, sourceRecord); ...... // Get the ActivityStack of the current focus final ActivityStack stack = mSupervisor.mFocusedStack; // If you start a new activity with a uid different from the activity currently in the resume state, check whether app switching is allowed if (voiceSession == null && (stack.getResumedActivity() == null || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) { if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, realCallingPid, realCallingUid, "Activity start")) { mController.addPendingActivityLaunch(new PendingActivityLaunch(r, sourceRecord, startFlags, stack, callerApp)); ActivityOptions.abort(checkedOptions); // Switch not allowed, return directly return ActivityManager.START_SWITCHES_CANCELED; } } ...... // Call overloaded method return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask, outActivity); }
> ActivityStarter.java private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) { int result = START_CANCELED; try { // Delay layout mService.mWindowManager.deferSurfaceLayout(); // Call startActivityUnchecked() method result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, outActivity); } finally { final ActivityStack stack = mStartActivity.getStack(); if (!ActivityManager.isStartResultSuccessful(result) && stack != null) { stack.finishActivityLocked(mStartActivity, RESULT_CANCELED, null /* intentResultData */, "startActivity", true /* oomAdj */); } // Restore layout mService.mWindowManager.continueSurfaceLayout(); } postStartActivityProcessing(r, result, mTargetStack); return result; }
Then call startActivityUnchecked().
> ActivityStarter.java private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) { // Set the initial state of starting Activity, including flag setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession, voiceInteractor); // Calculate mLaunchFlags, start flag bit computeLaunchingTaskFlags(); // Calculate mSourceStack computeSourceStack(); // Set start flag bit mIntent.setFlags(mLaunchFlags); // Find reusable activities ActivityRecord reusedActivity = getReusableIntentActivity(); ...... // Not equal to null indicates that the new activity should be inserted into the existing task stack if (reusedActivity != null) { if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(), (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) { Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode"); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } final boolean clearTopAndResetStandardLaunchMode = (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)) == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) && mLaunchMode == LAUNCH_MULTIPLE; if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) { mStartActivity.setTask(reusedActivity.getTask()); } if (reusedActivity.getTask().intent == null) { reusedActivity.getTask().setIntent(mStartActivity); } if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isDocumentLaunchesIntoExisting(mLaunchFlags) || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) { final TaskRecord task = reusedActivity.getTask(); // Clear task stack final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity, mLaunchFlags); if (reusedActivity.getTask() == null) { reusedActivity.setTask(task); } if (top != null) { if (top.frontOfTask) { top.getTask().setIntent(mStartActivity); } // Trigger onNewIntent() deliverNewIntent(top); } } ...... // Whether to create a new task boolean newTask = false; final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) ? mSourceRecord.getTask() : null; ...... // Top the Activity to be started in the Task mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition, mOptions); if (mDoResume) { final ActivityRecord topTaskActivity = mStartActivity.getTask().topRunningActivityLocked(); if (!mTargetStack.isFocusable() || (topTaskActivity != null && topTaskActivity.mTaskOverlay && mStartActivity != topTaskActivity)) { mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); mService.mWindowManager.executeAppTransition(); } else { if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityUnchecked"); } // Call ActivityStackSupervisor.resumeFocusedStackTopActivityLocked() method mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); } } else if (mStartActivity != null) { mSupervisor.mRecentTasks.add(mStartActivity.getTask()); } mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode, preferredLaunchDisplayId, mTargetStack); return START_SUCCESS; }
The startActivityUnchecked() method mainly deals with the start flag, the task stack to be started, etc. The source code of this block is very long, which has been greatly reduced, and only the basic call chain is retained. Interested students can view the source files by themselves. Next, the resumeFocusedStackTopActivityLocked() method of the ActivityStackSupervisor is called.
> ActivityStackSupervisor.java boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { if (!readyToResume()) { return false; } // Target Stack is mfocused Stack if (targetStack != null && isFocusedStack(targetStack)) { return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); } // Get the ActivityRecord at the top of the mFocusedStack stack final ActivityRecord r = mFocusedStack.topRunningActivityLocked(); if (r == null || !r.isState(RESUMED)) { mFocusedStack.resumeTopActivityUncheckedLocked(null, null); } else if (r.isState(RESUMED)) { // Kick off any lingering app transitions form the MoveTaskToFront operation. mFocusedStack.executeAppTransition(targetOptions); } return false; }
Get the ActivityStack of the Activity to be started and call its resumeTopActivityUncheckedLocked() method.
> ActivityStack.java boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { if (mStackSupervisor.inResumeTopActivity) { // Prevent recursive startup return false; } boolean result = false; try { mStackSupervisor.inResumeTopActivity = true; // Execute resumeTopActivityInnerLocked() method) result = resumeTopActivityInnerLocked(prev, options); final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); if (next == null || !next.canTurnScreenOn()) { checkReadyForSleep(); } } finally { mStackSupervisor.inResumeTopActivity = false; } return result; }
> ActivityStack.java private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { if (!mService.mBooting && !mService.mBooted) { // AMS has not been started yet return false; } ...... if (!hasRunningActivity) { // If there is no activity in the current stack, go to the next one. May launch Home app return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities"); } // next is the target Activity. Remove it from the following queues mStackSupervisor.mStoppingActivities.remove(next); mStackSupervisor.mGoingToSleepActivities.remove(next); next.sleeping = false; mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next); ...... // mResumedActivity refers to the current Activity if (mResumedActivity != null) { // When another Activity is in onResume(), pause it first pausing |= startPausingLocked(userLeaving, false, next, false); } ...... ActivityStack lastStack = mStackSupervisor.getLastStack(); if (next.app != null && next.app.thread != null) { ...... synchronized(mWindowManager.getWindowManagerLock()) { // This activity is now becoming visible. if (!next.visible || next.stopped || lastActivityTranslucent) { next.setVisibility(true); } ...... try { final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread, next.appToken); // Deliver all pending results. ArrayList<ResultInfo> a = next.results; if (a != null) { final int N = a.size(); if (!next.finishing && N > 0) { if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); transaction.addCallback(ActivityResultItem.obtain(a)); } } if (next.newIntents != null) { transaction.addCallback(NewIntentItem.obtain(next.newIntents, false /* andPause */)); } next.sleeping = false; mService.getAppWarningsLocked().onResumeActivity(next); mService.showAskCompatModeDialogLocked(next); next.app.pendingUiClean = true; next.app.forceProcessStateUpTo(mService.mTopProcessState); next.clearOptionsLocked(); transaction.setLifecycleStateRequest( ResumeActivityItem.obtain(next.app.repProcState, mService.isNextTransitionForward())); mService.getLifecycleManager().scheduleTransaction(transaction); } catch (Exception e) { next.setState(lastState, "resumeTopActivityInnerLocked"); // lastResumedActivity being non-null implies there is a lastStack present. if (lastResumedActivity != null) { lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked"); } Slog.i(TAG, "Restarting because process died: " + next); if (!next.hasBeenLaunched) { next.hasBeenLaunched = true; } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null && lastStack.isTopStackOnDisplay()) { next.showStartingWindow(null /* prev */, false /* newTask */, false /* taskSwitch */); } // Call startSpecificActivityLocked() mStackSupervisor.startSpecificActivityLocked(next, true, false); if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } } // From this point on, if something goes wrong there is no way // to recover the activity. try { next.completeResumeLocked(); } catch (Exception e) { ...... } } else { ...... // Call startSpecificActivityLocked() mStackSupervisor.startSpecificActivityLocked(next, true, true); } if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; }
Most of the code in the resumeTopActivityInnerLocked() method is omitted above. The original code is about 400 lines. Note the startPausingLocked() and startSpecificActivityLocked() methods.
Before you start an Activity, if the current Activity is in the onResume state, you need to pause it first, that is, call its onPause. This is the responsibility of the startPausingLocked() method. We will not analyze it in detail here, and we will write a separate article to explain the declaration cycle call of Activity. In addition, we need to execute the onPause of the current Activity before starting the target Activity, so we can't execute the time-consuming task in onPause, which will cause the Activity to be stuck when switching.
Another method, startSpecificActivityLocked(), is to start the specified Activity. Let's continue.
> ActivityStackSupervisor.java void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Find whether the process already exists through AMS ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); // Application process already exists and is bound if (app != null && app.thread != null) { try { if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) { app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode, mService.mProcessStats); } // Call realStartActivityLocked() when the application process already exists realStartActivityLocked(r, app, 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. } // Create process if application process does not exist mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); }
First, use AMS to find out whether the application process already exists. If it already exists and attach es, call realStartActivityLocked() to start the target Activity directly. If the application process does not exist, create the application process first.
stay In Android world, who wakes up Zygote? The creation process of the application process has been described. Let's briefly say that when the Zygote process is started, the LocalSocket server is enabled, waiting for the client's request. AMS sends a request to Zygote as the socket client. After Zygote receives the request, it fork s out the subprocess.
Today I see a very interesting question. Most IPC communication in Android is realized through Binder mechanism. Why does Zygote communicate across processes through socket? To be honest, I don't know. Welcome to leave your opinion.
Then there is realStartActivityLocked(), as the name implies, to start the Activity.
> ActivityStackSupervisor.java final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { if (!allPausedActivitiesComplete()) { // The new activity will not be started until all onPause() execution ends return false; } final TaskRecord task = r.getTask(); final ActivityStack stack = task.getStack(); beginDeferResume(); try { ...... // Update process oom adj value mService.updateLruProcessLocked(app, true, null); mService.updateOomAdjLocked(); try { ...... // Add LaunchActivityItem final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread, r.appToken); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, mService.isNextTransitionForward(), profilerInfo)); // Set lifecycle state final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); // A key // //Call clientlife cyclemanager. Scheduletransaction() mService.getLifecycleManager().scheduleTransaction(clientTransaction); ...... } catch (RemoteException e) { if (r.launchFailed) { // Second start failed, finish activity mService.appDiedLocked(app); stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, "2nd-crash", false); return false; } // First failure, restart process and try again r.launchFailed = true; app.activities.remove(r); throw e; } } finally { endDeferResume(); } r.launchFailed = false; ...... return true; }
The key point above is this code, mservice. Getlifecycle manager(). Scheduletransaction (clienttransaction).
Here we use ClientTransaction again. Remember the pause Activity mentioned above, which is also implemented through this class. I was going to write a separate article on the life cycle for further analysis. It seems that I can't escape. Here's a little bit of ClientTransaction.
First, mService.getLifecycleManager() returns the clientlifcyclemanager object, which is a new class in Android 9.0. Let's take a look at its scheduleTransaction() method.
> ClientLifecycleManager.java void scheduleTransaction(ClientTransaction transaction) throws RemoteException { final IApplicationThread client = transaction.getClient(); // -> ApplicationThread transaction.schedule(); // ClientTransaction if (!(client instanceof Binder)) { transaction.recycle(); } }
Follow up the schedule() method.
> ClientTransaction.java public void schedule() throws RemoteException { mClient.scheduleTransaction(this); }
Here, mClient is of iaapplicationthread type, which is the Binder proxy object of ApplicationThread, so it will call the ApplicationThread.scheduleTransaction() method across processes. ApplicationThread is the internal class of ActivityThread, but neither ApplicationThread nor ActivityThread has scheduleTransaction() method, so the method of its parent class ClientTransactionHandler is called.
> ClientTransactionHandler.java public abstract class ClientTransactionHandler { /** Prepare and schedule transaction for execution. */ void scheduleTransaction(ClientTransaction transaction) { transaction.preExecute(this); // The sendMessage() method is implemented in the ActivityThread class sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction); } }
Take a look at the sendMessage() method back in the ActivityThread class.
> ActivityThread.java private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous(true); } mH.sendMessage(msg); }
The execute ﹣ transaction message is sent to mH, and the transaction is carried. mH is a Handler class called H. It is responsible for the main thread message processing and defines about 50 kinds of events. Find out how it handles the execute? Transaction message.
> ActivityThread.java case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; // Execute TransactionExecutor.execute() mTransactionExecutor.execute(transaction); if (isSystem()) { transaction.recycle(); }
The TransactionExecutor's execute() method was called.
> TransactionExecutor.java` public void execute(ClientTransaction transaction) { final IBinder token = transaction.getActivityToken(); log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token); // Execute callBack executeCallbacks(transaction); // Execution lifecycle state executeLifecycleState(transaction); mPendingActions.clear(); log("End resolving transaction"); }
Let's first look at the executeCallbacks() method.
> TransactionExecutor.java @VisibleForTesting public void executeCallbacks(ClientTransaction transaction) { ...... final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); ...... item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); .... }
That's the core code. Execute the execute() and postExecute() methods of the incoming callback. Do you remember the parameters in the previous realStartActivityLocked() method calling addCallback()?
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), ......);
That is to say, the execute() method of LaunchActivityItem will be executed.
> LaunchActivityItem.java @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mIsForward, mProfilerInfo, client); // Call ActivityThread.handleLaunchActivity() client.handleLaunchActivity(r, pendingActions, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); }
Go around, go back to ActivityThread again, and execute its handleLaunchActivity() method.
> ActivityThread.java @Override public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { ...... final Activity a = performLaunchActivity(r, customIntent); ...... return a; }
> ActivityThread.java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } // Get ComponentName ComponentName component = r.intent.getComponent(); if (component == null) { component = r.intent.resolveActivity( mInitialApplication.getPackageManager()); r.intent.setComponent(component); } if (r.activityInfo.targetActivity != null) { component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } // Get Context ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); // Reflection create Activity activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { ...... } try { // Get Application Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) { CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (r.overrideConfig != null) { config.updateFrom(r.overrideConfig); } Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } appContext.setOuterContext(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); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; checkAndBlockForNetworkAccess(); activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { // set up themes activity.setTheme(theme); } activity.mCalled = false; // Execute onCreate() if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; } r.setState(ON_CREATE); mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { ...... } return activity; }
Here comes the figure of Instrumentation, which calls the newActivity() method and callActivityOnCreate() method respectively.
The newActivity() method reflects the creation of an Activity and calls its attach() method.
> Instrumentation.java public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException { Activity activity = (Activity)clazz.newInstance(); ActivityThread aThread = null; // Activity.attach expects a non-null Application Object. if (application == null) { application = new Application(); } activity.attach(context, aThread, this, token, 0 /* ident */, application, intent, info, title, parent, id, (Activity.NonConfigurationInstances)lastNonConfigurationInstance, new Configuration(), null /* referrer */, null /* voiceInteractor */, null /* window */, null /* activityConfigCallback */); return activity; }
The callActivityOnCreate() method calls the Activity.performCreate() method and finally calls the onCreate() method.
> Instrumentation.java public void callActivityOnCreate(Activity activity, Bundle icicle) { prePerformCreate(activity); activity.performCreate(icicle); postPerformCreate(activity); }
> Activity.java final void performCreate(Bundle icicle) { performCreate(icicle, null); } final void performCreate(Bundle icicle, PersistableBundle persistentState) { mCanEnterPictureInPicture = true; restoreHasCurrentPermissionRequest(icicle); // Callback onCreate() if (persistentState != null) { onCreate(icicle, persistentState); } else { onCreate(icicle); } writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate"); mActivityTransitionState.readState(icicle); mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); }
See here, there is a sense of relief, and finally to the onCreate() method. In fact, each life cycle callback of Activity is a similar call chain.
Do you remember which method traced all the way to onCreate? Is the execute() method of the TransactionExecutor.
> TransactionExecutor.java` public void execute(ClientTransaction transaction) { final IBinder token = transaction.getActivityToken(); log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token); // Execute callBack executeCallbacks(transaction); // Execution lifecycle state executeLifecycleState(transaction); mPendingActions.clear(); log("End resolving transaction"); }
In the previous analysis, executeCallBack() traces all the way to onCreate(). Next, we will analyze the executelifcyclestate() method.
> TransactionExecutor.java private void executeLifecycleState(ClientTransaction transaction) { final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest(); if (lifecycleItem == null) { // No lifecycle request, return early. return; } final IBinder token = transaction.getActivityToken(); final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); if (r == null) { // Ignore requests for non-existent client records for now. return; } // Cycle to the state right before the final requested state. cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */); // Execute the final transition with proper parameters. lifecycleItem.execute(mTransactionHandler, token, mPendingActions); lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions); }
I'm familiar with it, and I see lifecycleItem.execute(). The lifecycleItem here is still assigned in the realStartActivityLocked() method.
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
But before analyzing the ResumeActivityItem, notice the cycleToPath() method before the execute() method. The specific source code will not be analyzed. Its function is to synchronize with the upcoming lifecycle state according to the last executed lifecycle state. It's not so easy to understand. For example, the onCreate() method was called back last time. This time, the ResumeActivityItem is to be executed, and there is an onStart() state in the middle. Then the cycleToPath() method will call back onStart(), that is, the ActivityThread.handleStartActivity(). A call chain similar to handleLaunchActivity().
Then go back to ResumeActivityItem.execute().
> ResumeActivityItem.java @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume"); client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward, "RESUME_ACTIVITY"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); }
Still calling ActivityThread.handleResumeActivity(). But there is something special here. I have to carry it out.
The article begins with the WeChat public number: Bing Xin said, focus on Java, Android original knowledge sharing, LeetCode solution.
More new original articles, scan and follow me!
> ActivityThread.java public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { ...... r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) { // Visible page r.activity.makeVisible(); } } // Idler will be executed when the main thread is idle Looper.myQueue().addIdleHandler(new Idler()); }
The makeVisible() method makes DecorView visible.
> Activity.java void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
The last thing to notice is Looper.myQueue().addIdleHandler(new Idler()). Because of the length, I won't introduce it here. I will analyze it later when I write the Activity life cycle separately. You can first go to the source code to find the answer.
summary
All the way through the analysis, the Activity finally shows to the user.
In fact, the article is smelly and long. Many people may have questions. Is it really useful to read these articles? In my opinion, the two most important things for a programmer are basic skills and internal skills. Good basic skills can make us easily master a technology, and deep internal skills can make us solve problems. Source code can bring you, it is these.
Recently I read the source code of some components in Jetpack. The next article should be about Jetpack. Coming soon!
The article begins with the WeChat public number: Bing Xin said, focus on Java, Android original knowledge sharing, LeetCode solution.
More new original articles, scan and follow me!