preface
This is the fifth installment in the Android 9.0 AOSP series, so let’s review the previous installments.
The Pangu and Nuwa of the Java world — Zygote
This paper mainly introduces the startup process of Zygote, the first Java process in the Android world.
- Registers the server socket to respond to client requests
- Various preloading operations, classes, resources, shared libraries, etc
- Force GC once
- The fork SystemServer process
- Loop waiting for socket requests from clients (request socket connection and request fork application process)
The oldest son of the Zygote family, SystemServer
This paper mainly introduces the Zygote process fork first process SystemServer, it carries all kinds of system services to create and start.
-
Language, time zone, and region Settings
-
Vm memory Settings
-
Fingerprint information, Binder call Settings
-
PrepareMainLooper (), create the main thread Looper
-
Initialize the native service and load libandroid_servers.so
-
CreateSystemContext (), initializes the system context
-
Create the SystemServiceManager SystemServiceManager
-
StartBootstrapServices: Starts the system boot service
-
StartCoreServices: Starts the system core service
-
StartOtherServices, to startOtherServices
-
Looper.loop(), which starts the message loop
At the end of startOtherServices, AMS’s onSystemReady() method is called to start the desktop Activity.
Who woke up Zygote in the Android world?
This paper mainly introduces the process of AMS request to Zygote to create application process, that is, socket communication to Zygote process, echoing with the first article.
-
Call process.start () to create the application Process
-
ZygoteProcess is responsible for establishing a socket connection with the Zygote process and sending the parameters required to create the process to the Socket server of Zygote
-
Zygote server receives a parameter called after ZygoteConnection. ProcessOneCommand () processing parameters, and the process of the fork
-
Finally, findStaticMain() finds the Main() method of the ActivityThread class and executes, and the child process starts
ActivityManagerService, the ubiquitous system core service, starts process parsing
This paper introduces the startup process of ActivityManagerService (AMS), which is closely related to the startup, switching, scheduling and application process management of the four components.
-
AMS initialization, through ActivityManagerService. Lifecycle in the constructor of initialization
-
SetSystemProcess (), registers various services, creates ProcessRecord, updates the oOM_adj value
-
Installing the System Provider
-
SystemReady (), which will eventually start the desktop Home Activity
Today we are going to introduce the process of starting an Activity. Starting an Activity is a big project with lots of details. This article will briefly review the startup process without diving too deeply into source code details. Key issues, such as launchMode handling and lifecycle handling, will be dissected in a separate article.
Startup Process Analysis
Let’s start with a flow chart to make it easier to understand.
Following the previous analysis, ActivityManagerService’s systemReady() method finally starts the desktop Hme Activity, calling 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 the desktop ActivitymActivityStartController.startHomeActivity(intent, aInfo, myReason); }}else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
Copy the code
Call the 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(); }}Copy the code
The obtainStarter() method returns the ActivityStarter object, which is responsible for starting the Activity, a series of setXXX() methods that pass in the various parameters needed to start the Activity, and finally execute(), which is the actual startup logic.
Before moving on to the source code, what process are you in? AMS is initialized in the system_server process, so all the work above takes place in the system_server process. The startActivity() method we typically use during development is obviously called in the application process. What about the call chain for the normal startActivity() method? Check out the activity.startActivity () method.
> 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 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 {
/ / in the end is called Instrumentation. ExecStartActivity () method
if(options ! =null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode); }}}Copy the code
The execStartActivity() method of the Instrumentation is eventually called. Instrumentation is a very important class, Activity start, lifecycle callbacks are inseparable from it. You’ll encounter this class 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 the Activity
intresult = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! =null ? target.mEmbeddedID : null,
requestCode, 0.null, options);
// Check startup results
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
Copy the code
Here, the AMS startActivity() method is called with Binder. ActivityManager. GetService () don’t think it must be for AMS proxy objects.
> ActivityManager.java
public static IActivityManager getService(a) {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create(a) {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
returnam; }};Copy the code
We then go to the AMS 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 the ActivityStarter object
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute();
}
Copy the code
The next step is similar to starting the Home Activity before. Get the ActivityStarter object, provide the parameters, and finally execute().
ObtainStarter () gets the ActivityStarter object in factory mode.
ActivityStarter obtainStarter(Intent intent, String reason) {
return mFactory.obtain().setIntent(intent).setReason(reason);
}
Copy the code
The default implementation is mFactory 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 starters can be activated simultaneously. * /
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(a) {
// From SynchronizedPool
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); }}Copy the code
A synchronous object cache pool of capacity 3 is provided to cache ActivityStarter objects. The setXXX() methods are parameter configurations. Note that the setMayWait method sets the mayWait parameter to true. Let’s go straight to the actual execution, the execute() function.
> ActivityStarter.java
int execute(a) {
try {
if (mRequest.mayWait) { The setMayWait() method sets mayWait 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 {
returnstartActivity(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 {
Reclaim the current ActivityStarter objectonExecutionComplete(); }}Copy the code
StartActivityMayWait () is then called.
> 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!
// Create a new intent without modifying the original intent on the client
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 the ActivityInfo of the target Intent
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
synchronized (mService) {
finalActivityStack stack = mSupervisor.mFocusedStack; stack.mConfigWillChange = globalConfig ! =null&& mService.getGlobalConfiguration().diff(globalConfig) ! =0; .final ActivityRecord[] outRecord = new ActivityRecord[1];
// Call startActivity()
intres = 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 the startup result
outResult.result = res;
final ActivityRecord r = outRecord[0];
switch(res) {
case START_SUCCESS: {
mSupervisor.mWaitingActivityLaunched.add(outResult);
do {
try {
// Wait for the startup 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; }}}returnres; }}Copy the code
To start the Activity, invoke the startActivity() method, which has two overloaded methods called in turn. It waits for the startup result.
> 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) {
// If caller is not empty, find ProcessRecord through AMS
callerApp = mService.getRecordForAppLocked(caller);
if(callerApp ! =null) {
callingPid = callerApp.pid;
callingUid = callerApp.info.uid;
} else{ err = ActivityManager.START_PERMISSION_DENIED; }}// sourceRecord describes the Activity that initiated the request
// resultRecord User describes the Activity that receives startup results
// In general, the two activities should be the same
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null; .// Get the start flag
final intlaunchFlags = intent.getFlags(); .if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
// No class was found to handle the intent
err = ActivityManager.START_INTENT_NOT_RESOLVED;
}
if (err == ActivityManager.START_SUCCESS && aInfo == null) {
// The Activity class specified in the intent was not founderr = ActivityManager.START_CLASS_NOT_FOUND; }...// Check permissions
booleanabort = ! 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 = newActivityRecord(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 capture focus
final ActivityStack stack = mSupervisor.mFocusedStack;
// If you start a new activity with a different UID than the activity currently in resume state, check to see if 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);
// Do not allow switching, return directly
returnActivityManager.START_SWITCHES_CANCELED; }}...// Call overloaded methods
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
}
Copy the code
> 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()
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 the layout
mService.mWindowManager.continueSurfaceLayout();
}
postStartActivityProcessing(r, result, mTargetStack);
return result;
}
Copy the code
StartActivityUnchecked () is then called.
> 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 for starting the Activity, including flag
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
// Calculate mLaunchFlags
computeLaunchingTaskFlags();
/ / mSourceStack calculation
computeSourceStack();
// Set the boot flag bit
mIntent.setFlags(mLaunchFlags);
// Find reusable activitiesActivityRecord reusedActivity = getReusableIntentActivity(); .// Not 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 the 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 new tasks
boolean newTask = false;
finalTaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord ! =null)? mSourceRecord.getTask() :null; .// The Activity to be started is placed at the top of 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 () methodmSupervisor.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;
}
Copy the code
The startActivityUnchecked() method mainly handles startup flags, stacks of tasks to launch, etc. This piece of source code is very long, it has been heavily cut, only the basic call chain is retained. Interested students can check the source file for themselves. Then call the ActivityStackSupervisor resumeFocusedStackTopActivityLocked () method.
> ActivityStackSupervisor.java
boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if(! readyToResume()) {return false;
}
// The target Stack is mFocusedStack
if(targetStack ! =null && isFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
// Get the ActivityRecord at the top of the mFocusedStack
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;
}
Copy the code
Take after launch Activity ActivityStack 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;
/ / execution resumeTopActivityInnerLocked () method)
result = resumeTopActivityInnerLocked(prev, options);
final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
if (next == null || !next.canTurnScreenOn()) {
checkReadyForSleep();
}
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
return result;
}
Copy the code
> 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 Stack. The Home app might start
return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
}
// Next is the target Activity and removes it from the following queues
mStackSupervisor.mStoppingActivities.remove(next);
mStackSupervisor.mGoingToSleepActivities.remove(next);
next.sleeping = false; mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next); .// mResumedActivity indicates the current Activity
if(mResumedActivity ! =null) {
// When another Activity is in onResume(), suspend it
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;
}
Copy the code
Above omitted resumeTopActivityInnerLocked () method in most of the code, there are about more than four hundred lines of code. Which need to be aware of is startPausingLocked () and startSpecificActivityLocked () method.
Before starting an Activity, if the Activity is currently in the onResume state, you need to pause it by calling its onPause. This is what startPausingLocked() does. I won’t go into details here, but I’ll write a separate article explaining the Activity declaration cycle. In addition, the onPause of the current Activity is performed before the target Activity is started. Therefore, we cannot perform time-consuming tasks in the onPause, which will cause delays in switching activities.
Another method startSpecificActivityLocked () is to start the specified Activity, we continue to go down.
> ActivityStackSupervisor.java
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Use AMS to find if the process already exists
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
// The 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 a process if the application process does not exist
mService.startProcessLocked(r.processName, r.info.applicationInfo, true.0."activity", r.intent.getComponent(), false.false.true);
}
Copy the code
AMS first checks if the application process already exists, and if it exists and attaches, the target Activity is directly started by calling realStartActivityLocked(). If the application process does not exist, create it first.
Who woke up Zygote in the Android world? This section describes how to create an application process. The Zygote process starts with the LocalSocket server on, waiting for client requests. AMS sends a request to Zygote as a socket client, and Zygote forks out a child process after receiving the request.
Android IPC communication is mostly implemented with Binder mechanisms. Why Zygote uses sockets to communicate across processes? To be honest, I don’t know. I welcome your comments.
Next, realStartActivityLocked(), as its name suggests, actually starts the Activity.
> ActivityStackSupervisor.java
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
if(! allPausedActivitiesComplete()) {// A new activity is not started until all onPause() has been executed
return false;
}
final TaskRecord task = r.getTask();
final ActivityStack stack = task.getStack();
beginDeferResume();
try{...// Update the oom-adj value of the process
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 the life cycle state
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
/ / the key
/ / / / call ClientLifecycleManager. ScheduleTransaction ()mService.getLifecycleManager().scheduleTransaction(clientTransaction); . }catch (RemoteException e) {
if (r.launchFailed) {
// If the second startup fails, finish activity
mService.appDiedLocked(app);
stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null."2nd-crash".false);
return false;
}
// If the first attempt fails, restart the process and try again
r.launchFailed = true;
app.activities.remove(r);
throwe; }}finally {
endDeferResume();
}
r.launchFailed = false; .return true;
}
Copy the code
This is the focus of the code above, mService getLifecycleManager () scheduleTransaction (clientTransaction); .
ClientTransaction is used again. Remember that suspending an Activity is also done through this class. Originally prepared to write the life cycle of the separate article to re-analysis, it seems that still can not escape. Interspersed here is ClientTransaction.
First mService. GetLifecycleManager () returns the ClientLifecycleManager object, this is new in Android 9.0 class. Let’s look at its scheduleTransaction() method.
> ClientLifecycleManager.java
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient(); // -> ApplicationThread
transaction.schedule(); // ClientTransaction
if(! (clientinstanceofBinder)) { transaction.recycle(); }}Copy the code
Follow up schedule() method.
> ClientTransaction.java
public void schedule(a) throws RemoteException {
mClient.scheduleTransaction(this);
}
Copy the code
The mClient is of type IApplicationThread, which is the Binder proxy object for ApplicationThread. So there will be cross process calls to ApplicationThread. ScheduleTransaction () method. ApplicationThread is an inner class of ActivityThread, but neither ApplicationThread nor ActivityThread has a 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 classsendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction); }}Copy the code
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);
}
Copy the code
The EXECUTE_TRANSACTION message is sent to the mH and the transaction is carried with it. MH is a Handler class called H. It is responsible for main thread message processing and defines about fifty events. Find out how it handles the EXECUTE_TRANSACTION message.
> ActivityThread.java
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
/ / execution TransactionExecutor. The execute ()
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
Copy the code
The Execute () method of TransactionExecutor is called.
> TransactionExecutor.java`
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
/ / callBack execution
executeCallbacks(transaction);
// Execute the lifecycle state
executeLifecycleState(transaction);
mPendingActions.clear();
log("End resolving transaction");
}
Copy the code
Let’s start with the executeCallbacks() method.
> TransactionExecutor.java
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {...final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
finalClientTransactionItem item = callbacks.get(i); . item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); . }Copy the code
That’s the core code. Execute the execute() and postExecute() methods of the callback passed in. Remember the argument passed in from the call to addCallback() in realStartActivityLocked() earlier?
clientTransaction.addCallback(LaunchActivityItem.obtain(newIntent(r.intent), ......) ;Copy the code
That is, the Execute () method of the LaunchActivityItem is 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);
}
Copy the code
Go back to the ActivityThread and execute its handleLaunchActivity() method.
> ActivityThread.java
@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {...finalActivity a = performLaunchActivity(r, customIntent); .return a;
}
Copy the code
> 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 the 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 the Context
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// Reflection creates 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 {
/ / for 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 the theme
activity.setTheme(theme);
}
activity.mCalled = false;
/ / execution 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;
}
Copy the code
The new Instrumentation method and callActivityOnCreate() method are called respectively.
The newActivity() method reflects the creation of the 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;
}
Copy the code
The callActivityOnCreate() method calls the activity.performCreate () method, eventually calling back to the onCreate() method.
> Instrumentation.java
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
Copy the code
> 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());
}
Copy the code
There is a sense of relief that the onCreate() method has finally been executed. In fact, each lifecycle callback of an Activity is a similar invocation chain.
Remember how we traced it all the way to onCreate? Is the Execute () method of TransactionExecutor.
> TransactionExecutor.java`
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
/ / callBack execution
executeCallbacks(transaction);
// Execute the lifecycle state
executeLifecycleState(transaction);
mPendingActions.clear();
log("End resolving transaction");
}
Copy the code
From analyzing executeCallBack() all the way to onCreate(), we’ll analyze the executeLifecycleState() 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);
}
Copy the code
Lifecycleitem.execute (). LifecycleItem here is still assigned in realStartActivityLocked().
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
Copy the code
But before we look at the ResumeActivityItem, take a look at the cycleToPath() method before the execute() method. Specific source code will not go to the analysis, its role according to the last implementation of the life cycle state, and the upcoming implementation of the life cycle state synchronization. For example, last time we called back onCreate(), this time we’re going to do ResumeActivityItem with an onStart() state in the middle, So cycleToPath () method to callback onStart (), which is call ActivityThread. HandleStartActivity (). Call chain similar to handleLaunchActivity().
Then, back to ResumeActivityItem. The 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);
}
Copy the code
Is still call ActivityThread. HandleResumeActivity (). But there is one particular point here that I should mention.
Article first published wechat public account: Bingxin said, focus on Java, Android original knowledge sharing, LeetCode problem solving.
More latest original articles, scan code to pay attention to me!
> ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {... r.activity.mVisibleFromServer =true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
// The page is visibler.activity.makeVisible(); }}// Execute Idler when main thread is idle
Looper.myQueue().addIdleHandler(new Idler());
}
Copy the code
The makeVisible() method makes the DecorView visible.
> Activity.java
void makeVisible(a) {
if(! mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded =true;
}
mDecor.setVisibility(View.VISIBLE);
}
Copy the code
The last thing to notice is looper.myQueue ().addidleHandler (new Idler()). For space reasons, I won’t cover it here and will analyze it later when I write the Activity life cycle separately. You can go to the source code to find the answer.
conclusion
After all the analysis, the Activity is finally presented to the user.
The article is smelly and long, and many of you may wonder, is this really useful? In my opinion, the two most important things for a programmer are basic skills and internal skills. Good basic skills can make us easily grasp a skill, while deep internal skills can make us easily solve problems. This is what source code can bring you.
I’ve been looking at the source code for some of the components in Jetpack, so I think the next article will be about Jetpack. Stay tuned!
This article first published wechat official account: BingxinshuaiTM, focusing on Java, Android original knowledge sharing, LeetCode problem solving.
More latest original articles, scan code to pay attention to me!