Based on Android 10 source code analysis
preface
The analysis of the Activity launch process is mainly done with the following two pointcuts
- Analysis of the entire startup process
- Code tracking and process detail analysis.
Because our AMS Activity startup process is very complex, and the code flow is very jumping. If the analysis directly from our code will be more difficult to understand, so before entering the code flow analysis activity start process, we need to have a general understanding of the whole start process, so as to help us to analyze the source code.
Please go to the startup process of AMS
Overview of the Activity startup process
If the startup of an Activity is divided according to process differentiation, it can be divided into:
- The started Activity process does not exist, for example, a Launcher app
- The started Activity process exists
The difference between them and:
- AMS tells Zygote to fork a new app process when the Activity does not exist.
- After the app process starts, tell the AMS process to complete the fork and enter the follow-up Activity start process.
App startup process diagram
Communication mode
When you start a new app, there are many ways to communicate between processes. As shown in figure
Socket
Zygote creates a local Socket communication service ZygoteServer during process initialization. Zygote fork is notified through the socket.
//ZygoteInit.java
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;
Runnable caller;
try {
// Create a Socket service that communicates with the System Server process
zygoteServer = new ZygoteServer(isPrimaryZygote);
// If a child process is forked, a non-null caller is returned
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if(zygoteServer ! =null) { zygoteServer.closeServerSocket(); }}// We're in the child process and have exited the select loop. Proceed to execute the
// command.
// If caller! = null indicates that the current process is a child process. Execute the following method to enter ActivityThread
if(caller ! =null) { caller.run(); }}Copy the code
ZygoteServer. RunSelectLoop () will deal with all kinds of messages including socket connection, USAP
Binder communication
App and AMS communicate with each other through the Binder. When the app process forks, AMS needs to be told that the app process has been created successfully and the IApplicationThread Binder object is passed to AMS.
- App gets AMS Binder proxy
- App passes ApplicationThread Binder to AMS
- AMS saves the ApplicationThread Binder Proxy into ProcessRecord
Obtain the AMS Binder agent
Because AMS service at the time of initialization has to service registry to ServiceManager, so can pass ActivityManager. GetService () to obtain IActivityManager. Stub. The Proxy agent object, You can then communicate with the binder
//ActivityManager.java
public static IActivityManager getService(a) {
return IActivityManagerSingleton.get();
}
// Singleton mode
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);
return am;/ / return IActivityManager. Stub. Proxy Proxy objects}};Copy the code
Pass the ApplicationThread Binder object to AMS
ApplicationThread inherits from iApplicationThread.stub. After ApplicationThread Binder instance has been created, has not registered to the ServiceManager, AMS unable to actively seek our IApplicationThread. The Stub. The Proxy object, Therefore, we have to pass our Binder by way of reference.
//ActivityThread.java
@UnsupportedAppUsage
// create ApplicationThread Binder
final ApplicationThread mAppThread = new ApplicationThread();
public static void main(String[] args) {
/ /...
/ / 1. Create ActivityThread
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//....
}
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if(! system) {/ /...
//3. Obtain AMS binder proxy
final IActivityManager mgr = ActivityManager.getService();
try {
//4. The mAppThread binder parameter is passed to AMS
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
/ /...
} else {
/ / does not perform
}
/ /...
}
Copy the code
AMS saves the ApplicationThread Binder Proxy into ProcessRecord
Through the call binder, eventually into ActivityManagerService. AttachApplication
//ActivityManagerService.java
/ / note This thread is IApplicationThread. Styb. The Proxy object, after transformation Binder driver has to help us better
public final void attachApplication(IApplicationThread thread, long startSeq) {
/ /...
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
/ / 1. Call attachApplicationLockedattachApplicationLocked(thread, callingPid, callingUid, startSeq); Binder.restoreCallingIdentity(origId); }}private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
ProcessRecord app;
//....
try {
//2.AppDeathRecipient 保存 thread
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
/ / 3. AppDeathRecipient ProcessRecord kept
app.deathRecipient = adr;
} catch (RemoteException e) {
/ /...
return false;
}
/ /...
return true;
}
Copy the code
As shown in ProcessRecord, AMS saves the BINDER agent for the App process ApplicationThread. Later in the process you can communicate with the App process through ApplicationThread’s Binder agent.
Introduction to other core data classes
ProcessRecord
The interior of Android system is very complicated. After layers of encapsulation, APP only needs a few lines of simple code to complete the start/end and life cycle operations of any component.
When a component is started, it first needs to depend on the process, so the process needs to be created first, and the system needs to record each process, resulting in ProcessRecord.
Refer to the link
ActivityRecord
ActivityRecord, An entry in the history stack, representing An activity. An entry in the history stack that represents an activity.
Activity information is recorded in an ActivityRecord object
ActivityStack
ActivityStack, which internally maintains an ArrayList to manage ActivityRecords
ActivityStackSupervisor
ActivityStackSupervisor, as its name suggests, is used to manage ActivityStack
instrumentation
The official description
instrumentation can load both a test package and the application under test into the same process. Since the application components and their tests are in the same process, the tests can invoke methods in the components, and modify and examine fields in the components.
The translation
Instrumentation can load the test package and the target test application into the same process. Since each control and the test code are running in the same process, the test code can of course call the control’s methods and modify and validate some of the control’s data
Android instrumentation is a set of control methods or “hooks” in the Android system. These hooks can control the running of Android controls outside the normal life cycle (normal is controlled by the operating system), in fact, refers to the various process control methods provided by the Instrumentation class, the following table shows the relationship between some methods
Method | Control by User(Instrumentation) | Control by OS |
---|---|---|
onCreate | callActivityOnCreate | onCreate |
onDestroy | callActivityOnDestroy | onDestroy |
onStart | callActivityOnStart | onStart |
Refer to the link
When an Activity is started, the lifecycle of the Activity is called with Instrumentation
Activit start
If the Activity startup process is broken down by process, it can be broken down into the following steps:
Let’s take app process A starting app process B as an example
- Process A sends A command to AMS to dynamically start process B’s activity (triggered)
- AMS handles distribution startup commands (relay)
- If (process B does not exist) fork process B
- Notifies AMS B that the process is successfully created
- Send the start Activity command to process B
- If (process B does not exist) fork process B
- Process B handles AMS startup commands (process)
Trigger (entrance)
Activity::startActivityForResult
All of the acitivity prime will eventually go into this method
//Activity.java
public void startActivityForResult(
String who, Intent intent, int requestCode, @Nullable Bundle options) {
/ /...
// Initiate Actiivity by mInstrumentation
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
// When there is a result to process
if(ar ! =null) {
mMainThread.sendActivityResult(
mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
/ /...
}
Copy the code
MMainThread. GetApplicationThread () and ginseng ApplicationThread binder is wide
Instrumentation::execStartActivity
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
/ /...
try {
intent.migrateExtraStreamToClipData(who);
intent.prepareToLeaveProcess(who);
Obtain the ActivityManagerService agent 2. Start the activity
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getBasePackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
requestCode, 0.null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
Copy the code
ActivityTaskManager.getService()
Android 10 began the Activity will be start the service logic from AMS to ActivityTaskManagerService behind (ATMS). The logic is the same as before
public static IActivityTaskManager getService(a) {
return IActivityTaskManagerSingleton.get();
}
@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
new Singleton<IActivityTaskManager>() {
@Override
protected IActivityTaskManager create(a) {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
returnIActivityTaskManager.Stub.asInterface(b); }};Copy the code
Relay (process distribution start commands)
ActivityTaskManagerService.startActivity()
public final int startActivity(IApplicationThread caller, String callingPackage,
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions) {
/ / UserHandle getCallingUserId () by getting binder. The getCallngUid to obtain userId
return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
Copy the code
ActivityTaskManagerService.startActivityAsUser()
public int startActivityAsUser(IApplicationThread caller, String callingPackage,
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions, int userId) {
return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
true /*validateIncomingUser*/);
}
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
/ /...
//validateIncomingUser == true Verifies the userId
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
// Constructor design mode executes activitystarter.execute
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setCallingFeatureId(callingFeatureId)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setUserId(userId)
.execute();
}
Copy the code
GetActivityStartController (.) checkTargetUser () (ignore)
ValidateIncomingUser == True Verifies the userId
ActivityStartController.checkTargetUser()
. = > ActivityTaskManagerService handleIncomingUser () obtained by LocalServices ActivityManagerService. LocalService instance. As mentioned in the startup process of AMS, LocalServices cache the internal implementation of LocalService for each service
=>ActivityManagerService.LocalService.handleIncomingUser()
= > UserController. HandleIncomingUser () really perform validation, normal boot process returns in userId
ActivityStartController.obtainStarter()
The ActivityStarter is created in project mode. Mfactory.obtain () reuse ActivityStarter to reduce GC frequency in the same way that Handler message.obtain () does
ActivityStarter obtainStarter(Intent intent, String reason) {
return mFactory.obtain().setIntent(intent).setReason(reason);
}
Copy the code
ActivityStarter.execute()
int execute(a) {
try {
/ /...
// If the caller hasn't already resolved the activity, we're willing
// to do so here. If the caller is already holding the WM lock here,
// and we need to check dynamic Uri permissions, then we're forced
// to assume those permissions are denied to avoid deadlocking.
// Determine the Activity that can handle the start command.
if (mRequest.activityInfo == null) {
mRequest.resolveActivity(mSupervisor);
}
int res;
synchronized (mService.mGlobalLock) {
/ /...
// Start executing the request
res = executeRequest(mRequest);
Binder.restoreCallingIdentity(origId);
if (globalConfigWillChange) {
// If the caller also wants to switch to a new configuration, do so now.
// This allows a clean switch, as we are waiting for the current activity
// to pause (so we will not destroy it), and have not yet started the
// next activity.
mService.mAmInternal.enforceCallingPermission(
android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
if(stack ! =null) {
stack.mConfigWillChange = false;
}
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION,
"Updating to new configuration after starting activity.");
}
mService.updateConfigurationLocked(mRequest.globalConfig, null.false);
}
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
mLastStartActivityRecord);
return getExternalResult(mRequest.waitResult == null? res : waitForResult(res, mLastStartActivityRecord)); }}finally{ onExecutionComplete(); }}Copy the code
ActivityStarter.Request.resolveActivity()
void resolveActivity(ActivityStackSupervisor supervisor) {
/ /...
// Copy an intent, even if the intent changes ephemeralIntent
ephemeralIntent = new Intent(intent);
// Make sure that intent changes outside the method or changes here do not affect each other
intent = new Intent(intent);
/ /...
// Collect information about the target of the Intent.
For example, if we open a webView with an Intent and install multiple browsers, then the Request will be collected after this method is completed
activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
profilerInfo);
/ /...
}
Copy the code
ActivityStarter.executeRequest()
private int executeRequest(Request request) {
/ /...
// Find the callerApp
WindowProcessController callerApp = null;
if(caller ! =null) {
callerApp = mService.getProcessController(caller);
if(callerApp ! =null) {
callingPid = callerApp.getPid();
callingUid = callerApp.mInfo.uid;
} else {
Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid
+ ") when starting: "+ intent.toString()); err = ActivityManager.START_PERMISSION_DENIED; }}/ /... Omit some permission verification code
// Check whether the current application has permissions, our normal enabled must have permissions
booleanabort = ! mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode, callingPid, callingUid, callingPackage, callingFeatureId, request.ignoreTargetSecurity, inTask ! =null, callerApp, resultRecord, resultStack); abort |= ! mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); abort |= ! mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, callingPackage);// An Activity corresponds to an ActiityRecord, which is created here
final ActivityRecord r = newActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, callingFeatureId, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, request.componentSpecified, voiceSession ! =null, mSupervisor, checkedOptions,
sourceRecord);
/ / startActivityUnchecked execution
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
restrictedBgActivity, intentGrants);
return mLastStartActivityResult;
}
Copy the code
ActivityStarter.startActivityUnchecked()
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
int result = START_CANCELED;
final ActivityStack startedActivityStack;
try {
mService.deferWindowLayout();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
startedActivityStack = handleStartResult(r, result);
mService.continueWindowLayout();
}
postStartActivityProcessing(r, result, startedActivityStack);
return result;
}
Copy the code
ActivityStarter. StartActivityInner () is more important
All startup mode related processing is handled in this method
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
// Set the initialization information
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor, restrictedBgActivity);
// Determine the startup mode and append the corresponding flags to mLaunchFlags
computeLaunchingTaskFlags();
// Get the Activity startup stack
computeSourceStack();
// Based on the above calculation, set the flags recognized by the application
mIntent.setFlags(mLaunchFlags);
/ /... Omit some activityTask action code
mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(),
newTask, mKeepCurTransition, mOptions);
if (mDoResume) {// After handling the problem of starting the stack task stack, ready to implement the initiator's Resume state
final ActivityRecord topTaskActivity =
mStartActivity.getTask().topRunningActivityLocked();
if(! mTargetStack.isTopActivityFocusable() || (topTaskActivity ! =null&& topTaskActivity.isTaskOverlay() && mStartActivity ! = topTaskActivity)) {// If the activity is not focusable, we can't resume it, but still would like to
// make sure it becomes visible as it starts (this will also trigger entry
// animation). An example of this are PIP activities.
// Also, we don't want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
// Passing {@code null} as the start parameter ensures all activities are made
// visible.
mTargetStack.ensureActivitiesVisible(null /* starting */.0 /* configChanges */, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
} else {
// If the target stack was not previously focusable (previous top running activity
// on that stack was not visible) then any prior calls to move the stack to the
// will not update the focused stack. If starting the new activity now allows the
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
if(mTargetStack.isTopActivityFocusable() && ! mRootWindowContainer.isTopDisplayFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityInner");
}
Resume our mStartActivity from here
mRootWindowContainer.resumeFocusedStacksTopActivities(
mTargetStack, mStartActivity, mOptions);
}
}
mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
// Update the recent tasks list immediately when the activity starts
mSupervisor.mRecentTasks.add(mStartActivity.getTask());
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetStack);
return START_SUCCESS;
}
Copy the code
RootWindowContainer.resumeFocusedStacksTopActivities()
=>ActivityStack.resumeTopActivityUncheckedLocked()
. = > ActivityStack resumeTopActivityInnerLocked (), resume the Activity of the final execution in this method
ActivityStack. ResumeTopActivityInnerLocked () is more important
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
/ /...
Pausing == true Indicates that Pause is required
boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);
/ /... Omit the code, and here we do something based on pausing
/ /...
if (next.attachedToProcess()) {
/ /...
} else {
// Whoops, need to restart this activity!
if(! next.hasBeenLaunched) { next.hasBeenLaunched =true;
} else {
if (SHOW_APP_STARTING_PREVIEW) {
next.showStartingWindow(null /* prev */.false /* newTask */.false /* taskSwich */); }}// Start a specific Activity
mStackSupervisor.startSpecificActivity(next, true.true);
}
return true;
}
Copy the code
ActivityStackSupervisor.startSpecificActivity()
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
// Get the target process
final WindowProcessController wpc =
mService.getProcessController(r.processName, r.info.applicationInfo.uid);
boolean knownToBeDead = false;
if(wpc ! =null && wpc.hasThread()) {
try {
// If the process exists, proceed to the subsequent process
realStartActivityLocked(r, wpc, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
knownToBeDead = true;
}
r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
final boolean isTop = andResume && r.isTopRunningActivity();
// Start the process asynchronously if it does not exist
mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}
Copy the code
summary
Although the previous process is very complicated, but the summary is to start the Activity of some columns of verification, if the conditions are met into the later fork process or directly start the Activity of the target process
ActivityTaskManagerService.startProcessAsync()
void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
String hostingType) {
try {
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
+ activity.processName);
}
// Post message to start process to avoid possible deadlock of calling into AMS with the
// ATMS lock held.
/ / sent via handle executive function ActivityManagerInternal: : startProcess
final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
isTop, hostingType, activity.intent.getComponent());
mH.sendMessage(m);
} finally{ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }}Copy the code
ActivityManagerService.LocalService.startProcess()
=>ActivityManagerService.startProcess.startProcessLocked()
=>ProcessList.startProcessLocked()
ProcessList.startProcessLocked()
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
Runnable crashHandler) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord app;
//....
if (app == null) {// Execute here
checkSlow(startTime, "startProcess: creating new process record");
// New ProcessRecord Isolated == false
app = newProcessRecordLocked(info, processName, isolated, isolatedUid, hostingRecord);
if (app == null) {
Slog.w(TAG, "Failed making new process record for "
+ processName + "/" + info.uid + " isolated=" + isolated);
return null;
}
app.crashHandler = crashHandler;
app.isolatedEntryPoint = entryPoint;
app.isolatedEntryPointArgs = entryPointArgs;
if(precedence ! =null) {
app.mPrecedence = precedence;
precedence.mSuccessor = app;
}
checkSlow(startTime, "startProcess: done creating new process record");
} else {
//....
}
/ /...
//
final boolean success =
startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
return success ? app : null;
}
Copy the code
ProcessList.newProcessRecordLocked()
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid, HostingRecord hostingRecord) { String proc = customProcess ! =null ? customProcess : info.processName;
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
if (isolated) {
/ /... Isolated == false Do not execute here
}
final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);
/ /... Omit the initialization ProcessRecord code
// Add our ProcessRecord to the mProcessNames collection for easy access
addProcessNameLocked(r);
return r;
}
Copy the code
ProcessList.startProcessLocked()
boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
/ /... Some initialization of parameters
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
// Do this asynchronously
mService.mProcStartHandler.post(() -> handleProcessStart(
app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
requiredAbi, instructionSet, invokeWith, startSeq));
return true;
} else {
try {
final Process.ProcessStartResult startResult = startProcess(hostingRecord,
entryPoint, app,
uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
requiredAbi, instructionSet, invokeWith, startTime);
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
} catch (RuntimeException e) {
/ /... Boot failure
}
return app.pid > 0; }}Copy the code
ProcessList.startProcess()
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
int mountExternal, String seInfo, String requiredAbi, String instructionSet,
String invokeWith, long startTime) {
try {
/ /...
final Process.ProcessStartResult startResult;
if (hostingRecord.usesWebviewZygote()) {// Use webView Zygote, the code does not execute here
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else if (hostingRecord.usesAppZygote()) {// Using AppZygote, the code executes here
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
// We can't isolate app data and storage data as parent zygote already did that.
startResult = appZygote.getProcess().start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
false.false.new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {// Start the default process
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
return startResult;
} finally {
//....}}Copy the code
appZygote.getProcess().start() == ZygoteProcess.start()
= > ZygoteProcess. StartViaZygote () method will be called openZygoteSocketIfNeeded open ZygoteSocket (abi) method
=>ZygoteProcess.zygoteSendArgsAndGetResult()
=>ZygoteProcess.attemptZygoteSendArgsAndGetResult()
Notify ZygoteServer to start the service through the Socket
ZygoteServer.runSelectLoop()
Handling socket connections
Runnable runSelectLoop(String abiList) {
// Socket file descriptor
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
/ / socket connection
ArrayList<ZygoteConnection> peers = new ArrayList<>();
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
/ /...
while (true) {
StructPollfd[] pollFDs;
// Allocate enough space for the poll structs, taking into account
// the state of the USAP pool for this Zygote (could be a
// regular Zygote, a WebView Zygote, or an AppZygote).
if (mUsapPoolEnabled) {
usapPipeFDs = Zygote.getUsapPipeFDs();
pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
} else {
pollFDs = new StructPollfd[socketFDs.size()];
}
/* * For reasons of correctness the USAP pool pipe and event FDs * must be processed before the session and server sockets. This * is to ensure that the USAP pool accounting information is * accurate when handling other requests like API deny list * exemptions. */
int pollIndex = 0;
for (FileDescriptor socketFD : socketFDs) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = socketFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
final int usapPoolEventFDIndex = pollIndex;
/ /...
int pollReturnValue;
try {
// This will block, waiting for the pollTimeoutMs timeout.
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
if (pollReturnValue == 0) {
/ /... timeout
} else {
// Process all possible FD instructions
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {// Indicates that FD has no command to execute
continue;
}
if (pollIndex == 0) {
// Zygote server socket
// There is a new socket connection instruction to create a connection
ZygoteConnection newPeer = acceptCommandPeer(abiList);
// Add a socket connection
peers.add(newPeer);
// Add fd to socketFDs
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) {
// receive the fork command
try {
ZygoteConnection connection = peers.get(pollIndex);
// Fork. If command is not empty, command. Run will execute to ActivityThread.main
final Runnable command = connection.processOneCommand(this);
/ / connection. ProcessOneCommand (this) if it is the child will mIsForkChild set to true
if (mIsForkChild) {
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
if(command ! =null) {
throw new IllegalStateException("command ! = null");
}
if(connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(pollIndex); socketFDs.remove(pollIndex); }}}catch (Exception e) {
if(! mIsForkChild) { ZygoteConnection conn = peers.remove(pollIndex); conn.closeSocket(); socketFDs.remove(pollIndex); }else {
throwe; }}finally {
/ / reduction mIsForkChild
mIsForkChild = false; }}else {
/ /...}}/ /...
}
/ /...}}Copy the code
ZygoteConnection.processOneCommand
The place where the process is fork
Runnable processOneCommand(ZygoteServer zygoteServer) {
//....
int pid = -1; .//Fork the child process to get a new PID/fork child process, copy on write mode, execute once, return twice/// Pid =0 indicates that Zygote fork is successful
//pid > 0 indicates the true PID of the child process
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {
// in child
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
} else { / / Zygote process
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);
return null; }}finally {
/ /...}}Copy the code
ZygoteConnection.handleChildProc()
=>ZygoteInit.zygoteInit()
=>RuntimeInit.applicationInit()
=>RuntimeInit.findStaticMain()
. = > RuntimeInit MethodAndArgsCaller executive ActivityThread () returns. The main () a Runnable
=>ActivityThread.main()
=>ActivityThread.attach()
=>ActivityManagerService.attachApplication()
=>ActivityManagerService.attachApplicationLocked()
ActivityManagerService.attachApplication()
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
ProcessRecord app;
/ /...
// the mProcessesReady variable is assigned to true in AMS's systemReady,
// normalMode is true
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
/ /...
if (normalMode) {
try {
//mAtmInternal == LocalServices.getService(ActivityTaskManagerInternal.class);
/ / will eventually perform ActivityTaskManagerService. LocalService. AttachApplication ()
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
} catch (Exception e) {
/ /...}}/ /...
return true;
}
Copy the code
ActivityTaskManagerService.LocalService.attachApplication()
=>WindowManagerService.mRoot.attachApplication() == RootWindowContainer.attachApplication()
RootWindowContainer.attachApplication()
boolean attachApplication(WindowProcessController app) throws RemoteException {
final String processName = app.mName;
boolean didSomething = false;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
final ActivityStack stack = display.getFocusedStack();
if (stack == null) {
continue;
}
mTmpRemoteException = null;
mTmpBoolean = false; // Set to true if an activity was started.
final PooledFunction c = PooledLambda.obtainFunction(
RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity());
/ / execution WindowContainer. ForAllActivities ()
stack.forAllActivities(c);
c.recycle();
if(mTmpRemoteException ! =null) {
throw mTmpRemoteException;
}
didSomething |= mTmpBoolean;
}
/ /...
return didSomething;
}
Copy the code
WindowContainer. ForAllActivities () method of the total call RootWindowContainer: : startActivityForAttachedApplicationIfNeeded
RootWindowContainer.startActivityForAttachedApplicationIfNeeded()
private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r, WindowProcessController app, ActivityRecord top) {
/ /...
try {
/ / execution ActivityStackSupervisor. RealStartActivityLocked ()
if (mStackSupervisor.realStartActivityLocked(r, app, top == r /*andResume*/.true /*checkConfig*/)) {
mTmpBoolean = true; }}catch (RemoteException e) {
/ /...
return true;
}
return false;
}
Copy the code
ActivityStackSupervisor. StartSpecificActivity () method if the process inside also perform ActivityStackSupervisor. RealStartActivityLocked (), So the creation process has come full circle and the process behind it has come together again to actually start the Activity
ActivityStackSupervisor.realStartActivityLocked()
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
/ /...
try {
//....
try {
// Create activity launch transaction.
/ / create ClientTransaction
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
final DisplayContent dc = r.getDisplay().mDisplayContent;
//clientTransaction adds a callback
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
// Execute our transaction
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
/ /...
} catch (RemoteException e) {
//....}}finally {
/ /...
}
/ /...
return true;
}
Copy the code
ClientLifecycleManagerscheduleTransaction()
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
// start cross-process call
transaction.schedule();
if(! (clientinstanceofBinder)) { transaction.recycle(); }}Copy the code
ClientTransaction.schedule()
public void schedule(a) throws RemoteException {
//mClient is a proxy for ApplicationThread Binder
mClient.scheduleTransaction(this);
}
Copy the code
Processing (end)
ApplicationThread.scheduleTransaction()
= > ActivityThread. ScheduleTransaction () = = ClientTransactionHandler. ScheduleTransaction () ActivityThread inheritance ClientTransactionHandler
ClientTransactionHandler.scheduleTransaction()
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
// Switch to the main thread to handle the EXECUTE_TRANSACTION event via Handler
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
Copy the code
H.handleMessage()
public void handleMessage(Message msg) {
switch (msg.what) {
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
// The following code is in the main thread
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
break; }}Copy the code
TransactionExecutor.execute()
=>TransactionExecutor.executeCallbacks()
TransactionExecutor.executeCallbacks()
public void executeCallbacks(ClientTransaction transaction) {
// The callbacks collection here is what we will be executing
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
if (callbacks == null || callbacks.isEmpty()) {
return;
}
/ /...
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
// You may have some questions about what to do next. . This should return to ActivityStackSupervisor realStartActivityLocked () method we add LaunchActivityItem callback, So the following is executed to LaunchActivityItem. Executeitem.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); }}Copy the code
LaunchActivityItem.execute()
public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
// The client is the ActivityThread, so we get into familiar code
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
}
Copy the code
conclusion
Acitivity is a complex startup process that requires a general understanding of how it works so that it doesn’t get confused during these code jumps. Anyway, this is the end of the Activity launch process, and there are many other small details that are not covered too much for space.