🤔startActivity到底发生了什么?
startActivity
到底发生了什么?
大家好不好奇,我们平常使用的最多的startActivity
这个方法底层到底是什么样子的?本篇文章就为大家来解密!
本文主要源码的位置如下:
frameworks/base/services/core/java/com/android/server/wm
我们先根据下面的图从宏观上看一下startActivity
发生了什么.
一、App进程中
startActivity
方法的调用链如下:
在Activity
中:
startActivity
-> startActivityForResult
-> mInstrumentation.execStartActivity
-> ActivityTaskManager.getService().startActivity
.
ActivityTaskManager.getService()
是从单例对象中,通过binder
跨进程调用拿到的是一个IActivityTaskManager
, 它是ActivityTaskManagerService
的一个代理对象, 然后调动的是其startActivity
方法,然后后面的逻辑都是在系统进程完成的。
源码如下:
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = new Singleton<IActivityTaskManager>() { @Override protected IActivityTaskManager create() { final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE); return IActivityTaskManager.Stub.asInterface(b); } };
二、系统进程中
startActivity
快进程调用了ActivityTaskManagerService
的startActivityAsUser
方法。
我们查看一下源码:
ActivityTaskManagerService
的startActivityAsUser
方法,展开查看
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) { final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions); assertPackageMatchesCallingUid(callingPackage); enforceNotIsolatedCaller("startActivityAsUser"); if (isSdkSandboxActivityIntent(mContext, intent)) { SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager( SdkSandboxManagerLocal.class); sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity( intent, Binder.getCallingUid(), callingPackage ); } if (Process.isSdkSandboxUid(Binder.getCallingUid())) { SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager( SdkSandboxManagerLocal.class); if (sdkSandboxManagerLocal == null) { throw new IllegalStateException("SdkSandboxManagerLocal not found when starting" + " an activity from an SDK sandbox uid."); } sdkSandboxManagerLocal.enforceAllowedToStartActivity(intent); } userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser"); // TODO: Switch to user app stacks here. return getActivityStartController().obtainStarter(intent, "startActivityAsUser") .setCaller(caller) .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setResolvedType(resolvedType) .setResultTo(resultTo) .setResultWho(resultWho) .setRequestCode(requestCode) .setStartFlags(startFlags) .setProfilerInfo(profilerInfo) .setActivityOptions(opts) .setUserId(userId) .execute(); }
由源码可知,又调用了ActivityStartController
对象的obtainStarter
方法.
obtainStarter
源码如下
ActivityStarter obtainStarter(Intent intent, String reason) { return mFactory .obtain().setIntent(intent).setReason(reason); }
由源码可知通过链式调用返回了一个ActivityStarter
对象,所以obtainStarter
返回的ActivityStarter
也通过链式调用最后执行到execute
方法,我们查看下其源码:
ActivityStarter.java
int execute() { // Required for logging ContentOrFileUriEventReported in the finally block. String callerActivityName = null; ActivityRecord launchingRecord = null; try { onExecutionStarted(); if (mRequest.intent != null) { // Refuse possible leaked file descriptors if (mRequest.intent.hasFileDescriptors()) { throw new IllegalArgumentException("File descriptors passed in Intent"); } // Remove existing mismatch flag so it can be properly updated later mRequest.intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); } final LaunchingState launchingState; synchronized (mService.mGlobalLock) { final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo); final int callingUid = mRequest.realCallingUid == Request.DEFAULT_REAL_CALLING_UID ? Binder.getCallingUid() : mRequest.realCallingUid; launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching( mRequest.intent, caller, callingUid); callerActivityName = caller != null ? caller.info.name : null; } if (mRequest.intent != null) { mRequest.componentSpecified |= mRequest.intent.getComponent() != null; } // 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. if (mRequest.activityInfo == null) { mRequest.resolveActivity(mSupervisor); } // Add checkpoint for this shutdown or reboot attempt, so we can record the original // intent action and package name. if (mRequest.intent != null) { String intentAction = mRequest.intent.getAction(); String callingPackage = mRequest.callingPackage; if (intentAction != null && callingPackage != null && (Intent.ACTION_REQUEST_SHUTDOWN.equals(intentAction) || Intent.ACTION_SHUTDOWN.equals(intentAction) || Intent.ACTION_REBOOT.equals(intentAction))) { ShutdownCheckPoints.recordCheckPoint(intentAction, callingPackage, null); } } int res = START_CANCELED; synchronized (mService.mGlobalLock) { final boolean globalConfigWillChange = mRequest.globalConfig != null && mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0; final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (rootTask != null) { rootTask.mConfigWillChange = globalConfigWillChange; } ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config " + "will change = %b", globalConfigWillChange); final long origId = Binder.clearCallingIdentity(); try { res = resolveToHeavyWeightSwitcherIfNeeded(); if (res != START_SUCCESS) { return res; } res = executeRequest(mRequest); // 1 } finally { Binder.restoreCallingIdentity(origId); mRequest.logMessage.append(" result code=").append(res); Slog.i(TAG, mRequest.logMessage.toString()); mRequest.logMessage.setLength(0); } 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 (rootTask != null) { rootTask.mConfigWillChange = false; } ProtoLog.v(WM_DEBUG_CONFIGURATION, "Updating to new configuration after starting activity."); mService.updateConfigurationLocked(mRequest.globalConfig, null, false); } // The original options may have additional info about metrics. The mOptions is not // used here because it may be cleared in setTargetRootTaskIfNeeded. final ActivityOptions originalOptions = mRequest.activityOptions != null ? mRequest.activityOptions.getOriginalOptions() : null; // Only track the launch time of activity that will be resumed. launchingRecord = mDoResume ? mLastStartActivityRecord : null; // If the new record is the one that started, a new activity has created. final boolean newActivityCreated = mStartActivity == launchingRecord; // Notify ActivityMetricsLogger that the activity has launched. // ActivityMetricsLogger will then wait for the windows to be drawn and populate // WaitResult. mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res, newActivityCreated, launchingRecord, originalOptions); if (mRequest.waitResult != null) { mRequest.waitResult.result = res; res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord, launchingState); } return getExternalResult(res); } } finally { // Notify UriGrantsManagerService that activity launch completed. Required for logging // the ContentOrFileUriEventReported message. mSupervisor.mService.mUgmInternal.notifyActivityLaunchRequestCompleted( mRequest.hashCode(), // isSuccessfulLaunch launchingRecord != null, // Intent action mRequest.intent != null ? mRequest.intent.getAction() : null, mRequest.realCallingUid, callerActivityName, // Callee UID mRequest.activityInfo != null ? mRequest.activityInfo.applicationInfo.uid : INVALID_UID, // Callee Activity name mRequest.activityInfo != null ? mRequest.activityInfo.name : null, // isStartActivityForResult launchingRecord != null && launchingRecord.resultTo != null); onExecutionComplete(); } }
位置1处是真正完成Activity
启动流程的代码。
其源码如下:
ActivityStarter.java
/** * Executing activity start request and starts the journey of starting an activity. Here * begins with performing several preliminary checks. The normally activity launch flow will * go through {@link #startActivityUnchecked} to {@link #startActivityInner}. */ private int executeRequest(Request request) { if (TextUtils.isEmpty(request.reason)) { throw new IllegalArgumentException("Need to specify a reason."); } mLastStartReason = request.reason; mLastStartActivityTimeMs = System.currentTimeMillis(); final IApplicationThread caller = request.caller; Intent intent = request.intent; NeededUriGrants intentGrants = request.intentGrants; String resolvedType = request.resolvedType; ActivityInfo aInfo = request.activityInfo; ResolveInfo rInfo = request.resolveInfo; final IVoiceInteractionSession voiceSession = request.voiceSession; final IBinder resultTo = request.resultTo; String resultWho = request.resultWho; int requestCode = request.requestCode; int callingPid = request.callingPid; int callingUid = request.callingUid; String callingPackage = request.callingPackage; String callingFeatureId = request.callingFeatureId; final int realCallingPid = request.realCallingPid; final int realCallingUid = request.realCallingUid; final int startFlags = request.startFlags; final SafeActivityOptions options = request.activityOptions; Task inTask = request.inTask; TaskFragment inTaskFragment = request.inTaskFragment; int err = ActivityManager.START_SUCCESS; // Pull the optional Ephemeral Installer-only bundle out of the options early. final Bundle verificationBundle = options != null ? options.popAppVerificationBundle() : null; 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 = START_PERMISSION_DENIED; } } final int userId = aInfo != null && aInfo.applicationInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0; final int launchMode = aInfo != null ? aInfo.launchMode : 0; if (err == ActivityManager.START_SUCCESS) { request.logMessage.append("START u").append(userId).append(" {") .append(intent.toShortString(true, true, true, false)) .append("} with ").append(launchModeToString(launchMode)) .append(" from uid ").append(callingUid); if (callingUid != realCallingUid && realCallingUid != Request.DEFAULT_REAL_CALLING_UID) { request.logMessage.append(" (realCallingUid=").append(realCallingUid).append(")"); } } ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; if (resultTo != null) { sourceRecord = ActivityRecord.isInAnyTask(resultTo); if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord); } if (sourceRecord != null) { if (requestCode >= 0 && !sourceRecord.finishing) { resultRecord = sourceRecord; } } } final int launchFlags = intent.getFlags(); if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) { // Transfer the result target from the source activity to the new one being started, // including any failures. if (requestCode >= 0) { SafeActivityOptions.abort(options); return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; } resultRecord = sourceRecord.resultTo; if (resultRecord != null && !resultRecord.isInRootTaskLocked()) { resultRecord = null; } resultWho = sourceRecord.resultWho; requestCode = sourceRecord.requestCode; sourceRecord.resultTo = null; if (resultRecord != null) { resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode); } if (sourceRecord.launchedFromUid == callingUid) { // The new activity is being launched from the same uid as the previous activity // in the flow, and asking to forward its result back to the previous. In this // case the activity is serving as a trampoline between the two, so we also want // to update its launchedFromPackage to be the same as the previous activity. // Note that this is safe, since we know these two packages come from the same // uid; the caller could just as well have supplied that same package name itself // . This specifially deals with the case of an intent picker/chooser being // launched in the app flow to redirect to an activity picked by the user, where // we want the final activity to consider it to have been launched by the // previous app activity. callingPackage = sourceRecord.launchedFromPackage; callingFeatureId = sourceRecord.launchedFromFeatureId; } } if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) { // We couldn't find a class that can handle the given Intent. // That's the end of that! err = ActivityManager.START_INTENT_NOT_RESOLVED; } if (err == ActivityManager.START_SUCCESS && aInfo == null) { // We couldn't find the specific class specified in the Intent. err = ActivityManager.START_CLASS_NOT_FOUND; if (isArchivingEnabled()) { PackageArchiver packageArchiver = mService .getPackageManagerInternalLocked() .getPackageArchiver(); if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) { err = packageArchiver .requestUnarchiveOnActivityStart( intent, callingPackage, mRequest.userId, realCallingUid); } } } if (err == ActivityManager.START_SUCCESS && sourceRecord != null && sourceRecord.getTask().voiceSession != null) { // If this activity is being launched as part of a voice session, we need to ensure // that it is safe to do so. If the upcoming activity will also be part of the voice // session, we can only launch it if it has explicitly said it supports the VOICE // category, or it is a part of the calling app. if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) { try { intent.addCategory(Intent.CATEGORY_VOICE); if (!mService.getPackageManager().activitySupportsIntentAsUser( intent.getComponent(), intent, resolvedType, userId)) { Slog.w(TAG, "Activity being started in current voice task does not support " + "voice: " + intent); err = ActivityManager.START_NOT_VOICE_COMPATIBLE; } } catch (RemoteException e) { Slog.w(TAG, "Failure checking voice capabilities", e); err = ActivityManager.START_NOT_VOICE_COMPATIBLE; } } } if (err == ActivityManager.START_SUCCESS && voiceSession != null) { // If the caller is starting a new voice session, just make sure the target // is actually allowing it to run this way. try { if (!mService.getPackageManager().activitySupportsIntentAsUser( intent.getComponent(), intent, resolvedType, userId)) { Slog.w(TAG, "Activity being started in new voice task does not support: " + intent); err = ActivityManager.START_NOT_VOICE_COMPATIBLE; } } catch (RemoteException e) { Slog.w(TAG, "Failure checking voice capabilities", e); err = ActivityManager.START_NOT_VOICE_COMPATIBLE; } } final Task resultRootTask = resultRecord == null ? null : resultRecord.getRootTask(); if (err != START_SUCCESS) { if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED, null /* data */, null /* callerToken */, null /* dataGrants */); } SafeActivityOptions.abort(options); return err; } boolean abort; try { abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode, callingPid, callingUid, callingPackage, callingFeatureId, request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultRootTask); } catch (SecurityException e) { // Return activity not found for the explicit intent if the caller can't see the target // to prevent the disclosure of package existence. final Intent originalIntent = request.ephemeralIntent; if (originalIntent != null && (originalIntent.getComponent() != null || originalIntent.getPackage() != null)) { final String targetPackageName = originalIntent.getComponent() != null ? originalIntent.getComponent().getPackageName() : originalIntent.getPackage(); if (mService.getPackageManagerInternalLocked() .filterAppAccess(targetPackageName, callingUid, userId)) { if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED, null /* data */, null /* callerToken */, null /* dataGrants */); } SafeActivityOptions.abort(options); return ActivityManager.START_CLASS_NOT_FOUND; } } throw e; } abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, callingPackage); // Merge the two options bundles, while realCallerOptions takes precedence. ActivityOptions checkedOptions = options != null ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null; final BalVerdict balVerdict; if (!abort) { try { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "shouldAbortBackgroundActivityStart"); BackgroundActivityStartController balController = mSupervisor.getBackgroundActivityLaunchController(); balVerdict = balController.checkBackgroundActivityStart( callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, request.originatingPendingIntent, request.forcedBalByPiSender, resultRecord, intent, checkedOptions); request.logMessage.append(" (").append(balVerdict).append(")"); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } else { // Sets ALLOW_BY_DEFAULT as default value as the activity launch will be aborted anyway. balVerdict = BalVerdict.ALLOW_BY_DEFAULT; } if (request.allowPendingRemoteAnimationRegistryLookup) { checkedOptions = mService.getActivityStartController() .getPendingRemoteAnimationRegistry() .overrideOptionsIfNeeded(callingPackage, checkedOptions); } if (mService.mController != null) { try { // The Intent we give to the watcher has the extra data stripped off, since it // can contain private information. Intent watchIntent = intent.cloneFilter(); abort |= !mService.mController.activityStarting(watchIntent, aInfo.applicationInfo.packageName); } catch (RemoteException e) { mService.mController = null; } } final TaskDisplayArea suggestedLaunchDisplayArea = computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions); mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage, callingFeatureId); if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) { // activity start was intercepted, e.g. because the target user is currently in quiet // mode (turn off work) or the target application is suspended intent = mInterceptor.mIntent; rInfo = mInterceptor.mRInfo; aInfo = mInterceptor.mAInfo; resolvedType = mInterceptor.mResolvedType; inTask = mInterceptor.mInTask; callingPid = mInterceptor.mCallingPid; callingUid = mInterceptor.mCallingUid; checkedOptions = mInterceptor.mActivityOptions; // The interception target shouldn't get any permission grants // intended for the original destination intentGrants = null; } if (abort) { if (resultRecord != null) { resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED, null /* data */, null /* callerToken */, null /* dataGrants */); } // We pretend to the caller that it was really started, but they will just get a // cancel result. ActivityOptions.abort(checkedOptions); return START_ABORTED; } // If permissions need a review before any of the app components can run, we // launch the review activity and pass a pending intent to start the activity // we are to launching now after the review is completed. if (aInfo != null) { if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( aInfo.packageName, userId)) { final IIntentSender target = mService.getIntentSenderLocked( ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId, callingUid, userId, null, null, 0, new Intent[]{intent}, new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT, null); Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); int flags = intent.getFlags(); flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; /* * Prevent reuse of review activity: Each app needs their own review activity. By * default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities * with the same launch parameters (extras are ignored). Hence to avoid possible * reuse force a new activity via the MULTIPLE_TASK flag. * * Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used, * hence no need to add the flag in this case. */ if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) { flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK; } newIntent.setFlags(flags); newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName); newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target)); if (resultRecord != null) { newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true); } intent = newIntent; // The permissions review target shouldn't get any permission // grants intended for the original destination intentGrants = null; resolvedType = null; callingUid = realCallingUid; callingPid = realCallingPid; rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0, computeResolveFilterUid( callingUid, realCallingUid, request.filterCallingUid), realCallingPid); aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/); if (DEBUG_PERMISSIONS_REVIEW) { final Task focusedRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false) + "} from uid " + callingUid + " on display " + (focusedRootTask == null ? DEFAULT_DISPLAY : focusedRootTask.getDisplayId())); } } } // If we have an ephemeral app, abort the process of launching the resolved intent. // Instead, launch the ephemeral installer. Once the installer is finished, it // starts either the intent we resolved here [on install error] or the ephemeral // app [on install success]. if (rInfo != null && rInfo.auxiliaryInfo != null) { intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent, callingPackage, callingFeatureId, verificationBundle, resolvedType, userId); resolvedType = null; callingUid = realCallingUid; callingPid = realCallingPid; // The ephemeral installer shouldn't get any permission grants // intended for the original destination intentGrants = null; aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/); } // TODO (b/187680964) Correcting the caller/pid/uid when start activity from shortcut // Pending intent launched from systemui also depends on caller app if (callerApp == null && realCallingPid > 0) { final WindowProcessController wpc = mService.mProcessMap.getProcess(realCallingPid); if (wpc != null) { callerApp = wpc; } } final ActivityRecord r = new ActivityRecord.Builder(mService) .setCaller(callerApp) .setLaunchedFromPid(callingPid) .setLaunchedFromUid(callingUid) .setLaunchedFromPackage(callingPackage) .setLaunchedFromFeature(callingFeatureId) .setIntent(intent) .setResolvedType(resolvedType) .setActivityInfo(aInfo) .setConfiguration(mService.getGlobalConfiguration()) .setResultTo(resultRecord) .setResultWho(resultWho) .setRequestCode(requestCode) .setComponentSpecified(request.componentSpecified) .setRootVoiceInteraction(voiceSession != null) .setActivityOptions(checkedOptions) .setSourceRecord(sourceRecord) .build(); mLastStartActivityRecord = r; if (r.appTimeTracker == null && sourceRecord != null) { // If the caller didn't specify an explicit time tracker, we want to continue // tracking under any it has. r.appTimeTracker = sourceRecord.appTimeTracker; } // Only allow app switching to be resumed if activity is not a restricted background // activity and target app is not home process, otherwise any background activity // started in background task can stop home button protection mode. // As the targeted app is not a home process and we don't need to wait for the 2nd // activity to be started to resume app switching, we can just enable app switching // directly. WindowProcessController homeProcess = mService.mHomeProcess; boolean isHomeProcess = homeProcess != null && aInfo.applicationInfo.uid == homeProcess.mUid; if (balVerdict.allows() && !isHomeProcess) { mService.resumeAppSwitches(); } // Only do the create here since startActivityInner can abort. If it doesn't abort, // the requestStart will be sent in handleStartRequest. final Transition newTransition = r.mTransitionController.isShellTransitionsEnabled() ? r.mTransitionController.createAndStartCollecting(TRANSIT_OPEN) : null; // Because startActivity must run immediately, it can get combined with another // transition meaning it is no-longer independent. This is NOT desirable, but is the // only option for the time being. final boolean isIndependent = newTransition != null; final Transition transition = isIndependent ? newTransition : mService.getTransitionController().getCollectingTransition(); // 1 mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession, request.voiceInteractor, startFlags, checkedOptions, inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid, transition, isIndependent); if (request.outActivity != null) { request.outActivity[0] = mLastStartActivityRecord; } return mLastStartActivityResult; }
有文档注释可知:
startActivityUnchecked
-> startActivityInner
startActivityUnchecked
源码如下:
ActivityStarter.java
/** * Start an activity while most of preliminary checks has been done and caller has been * confirmed that holds necessary permissions to do so. * Here also ensures that the starting activity is removed if the start wasn't successful. */ private int startActivityUnchecked( final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, ActivityOptions options, Task inTask, TaskFragment inTaskFragment, BalVerdict balVerdict, NeededUriGrants intentGrants, int realCallingUid, Transition transition, boolean isIndependentLaunch) { int result = START_CANCELED; final Task startedActivityRootTask; RemoteTransition remoteTransition = r.takeRemoteTransition(); // Create a display snapshot as soon as possible. if (isIndependentLaunch && mRequest.freezeScreen) { final TaskDisplayArea tda = mLaunchParams.hasPreferredTaskDisplayArea() ? mLaunchParams.mPreferredTaskDisplayArea : mRootWindowContainer.getDefaultTaskDisplayArea(); final DisplayContent dc = mRootWindowContainer.getDisplayContentOrCreate( tda.getDisplayId()); if (dc != null) { transition.collect(dc); transition.collectVisibleChange(dc); } } try { mService.deferWindowLayout(); r.mTransitionController.collect(r); try { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner"); result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor, // 1 startFlags, options, inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid); } catch (Exception ex) { Slog.e(TAG, "Exception on startActivityInner", ex); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); startedActivityRootTask = handleStartResult(r, options, result, isIndependentLaunch, remoteTransition, transition); } } finally { mService.continueWindowLayout(); } postStartActivityProcessing(r, result, startedActivityRootTask); return result; }
startActivityInner
源码如下:
ActivityStarter.java
/** * Start an activity and determine if the activity should be adding to the top of an existing * task or delivered new intent to an existing activity. Also manipulating the activity task * onto requested or valid root-task/display. * * Note: This method should only be called from {@link #startActivityUnchecked}. */ // TODO(b/152429287): Make it easier to exercise code paths through startActivityInner @VisibleForTesting int startActivityInner( final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, ActivityOptions options, Task inTask, TaskFragment inTaskFragment, BalVerdict balVerdict, NeededUriGrants intentGrants, int realCallingUid) { setInitialState(r, options, inTask, inTaskFragment, startFlags, sourceRecord, voiceSession, voiceInteractor, balVerdict.getCode(), realCallingUid); computeLaunchingTaskFlags(); mIntent.setFlags(mLaunchFlags); boolean dreamStopping = false; for (ActivityRecord stoppingActivity : mSupervisor.mStoppingActivities) { if (stoppingActivity.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_DREAM) { dreamStopping = true; break; } } // Get top task at beginning because the order may be changed when reusing existing task. final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask(); final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null; final boolean sourceActivityLaunchedFromBubble = sourceRecord != null && sourceRecord.getLaunchedFromBubble(); // if the flag is enabled, allow reusing bubbled tasks only if the source activity is // bubbled. final boolean includeLaunchedFromBubble = Flags.onlyReuseBubbledTaskWhenLaunchedFromBubble() ? sourceActivityLaunchedFromBubble : true; final Task reusedTask = resolveReusableTask(includeLaunchedFromBubble); // If requested, freeze the task list if (mOptions != null && mOptions.freezeRecentTasksReordering() && mSupervisor.mRecentTasks.isCallerRecents(r.launchedFromUid) && !mSupervisor.mRecentTasks.isFreezeTaskListReorderingSet()) { mFrozeTaskList = true; mSupervisor.mRecentTasks.setFreezeTaskListReordering(); } // Compute if there is an existing task that should be used for. final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask(); final boolean newTask = targetTask == null; mTargetTask = targetTask; computeLaunchParams(r, sourceRecord, targetTask); // Check if starting activity on given task or on a new task is allowed. int startResult = isAllowedToStart(r, newTask, targetTask); if (startResult != START_SUCCESS) { if (r.resultTo != null) { r.resultTo.sendResult(INVALID_UID, r.resultWho, r.requestCode, RESULT_CANCELED, null /* data */, null /* callerToken */, null /* dataGrants */); } return startResult; } if (targetTask != null) { if (targetTask.getTreeWeight() > MAX_TASK_WEIGHT_FOR_ADDING_ACTIVITY) { Slog.e(TAG, "Remove " + targetTask + " because it has contained too many" + " activities or windows (abort starting " + r + " from uid=" + mCallingUid); targetTask.removeImmediately("bulky-task"); return START_ABORTED; } // When running transient transition, the transient launch target should keep on top. // So disallow the transient hide activity to move itself to front, e.g. trampoline. if (!avoidMoveToFront() && (mService.mHomeProcess == null || mService.mHomeProcess.mUid != realCallingUid) && (prevTopTask != null && prevTopTask.isActivityTypeHomeOrRecents()) && r.mTransitionController.isTransientHide(targetTask)) { mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY; } // If the activity is started by sending a pending intent and only its creator has the // privilege to allow BAL (its sender does not), avoid move it to the front. Only do // this when it is not a new task and not already been marked as avoid move to front. // Guarded by a flag: balDontBringExistingBackgroundTaskStackToFg if (balDontBringExistingBackgroundTaskStackToFg() && !avoidMoveToFront() && balVerdict.onlyCreatorAllows()) { mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS; } mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask()); } final ActivityRecord targetTaskTop = newTask ? null : targetTask.getTopNonFinishingActivity(); if (targetTaskTop != null) { // Removes the existing singleInstance activity in another task (if any) while // launching a singleInstance activity on sourceRecord's task. if (LAUNCH_SINGLE_INSTANCE == mLaunchMode && mSourceRecord != null && targetTask == mSourceRecord.getTask()) { final ActivityRecord activity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info, false); if (activity != null && activity.getTask() != targetTask) { activity.destroyIfPossible("Removes redundant singleInstance"); } } if (mLastStartActivityRecord != null) { targetTaskTop.mLaunchSourceType = mLastStartActivityRecord.mLaunchSourceType; } targetTaskTop.mTransitionController.collect(targetTaskTop); recordTransientLaunchIfNeeded(targetTaskTop); // Recycle the target task for this launch. startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants, balVerdict); if (startResult != START_SUCCESS) { return startResult; } } else { mAddingToTask = true; } // If the activity being launched is the same as the one currently at the top, then // we need to check if it should only be launched once. final Task topRootTask = mPreferredTaskDisplayArea.getFocusedRootTask(); if (topRootTask != null) { startResult = deliverToCurrentTopIfNeeded(topRootTask, intentGrants); if (startResult != START_SUCCESS) { return startResult; } } if (mTargetRootTask == null) { mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask, mOptions); } if (newTask) { final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) ? mSourceRecord.getTask() : null; setNewTask(taskToAffiliate); } else if (mAddingToTask) { addOrReparentStartingActivity(targetTask, "adding to task"); } // After activity is attached to task, but before actual start recordTransientLaunchIfNeeded(mLastStartActivityRecord); if (mDoResume) { if (!avoidMoveToFront()) { mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask); if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming() && !dreamStopping) { // Launching underneath dream activity (fullscreen, always-on-top). Run the // launch--behind transition so the Activity gets created and starts // in visible state. mLaunchTaskBehind = true; r.mLaunchTaskBehind = true; } } else { logPIOnlyCreatorAllowsBAL(); } } mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants, mStartActivity.getUriPermissionsLocked()); if (mStartActivity.resultTo != null && mStartActivity.resultTo.info != null) { // we need to resolve resultTo to a uid as grantImplicitAccess deals explicitly in UIDs final PackageManagerInternal pmInternal = mService.getPackageManagerInternalLocked(); final int resultToUid = pmInternal.getPackageUid( mStartActivity.resultTo.info.packageName, 0 /* flags */, mStartActivity.mUserId); pmInternal.grantImplicitAccess(mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) /*recipient*/, resultToUid /*visible*/, true /*direct*/); } else if (mStartActivity.mShareIdentity) { final PackageManagerInternal pmInternal = mService.getPackageManagerInternalLocked(); pmInternal.grantImplicitAccess(mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) /*recipient*/, r.launchedFromUid /*visible*/, true /*direct*/); } final Task startedTask = mStartActivity.getTask(); if (newTask) { EventLogTags.writeWmCreateTask(mStartActivity.mUserId, startedTask.mTaskId, startedTask.getRootTaskId(), startedTask.getDisplayId()); } mStartActivity.logStartActivity(EventLogTags.WM_CREATE_ACTIVITY, startedTask); mStartActivity.getTaskFragment().clearLastPausedActivity(); mRootWindowContainer.startPowerModeLaunchIfNeeded( false /* forceSend */, mStartActivity); final boolean isTaskSwitch = startedTask != prevTopTask; mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch, mOptions, sourceRecord); if (mDoResume) { final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked(); if (!mTargetRootTask.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. mTargetRootTask.ensureActivitiesVisible(null /* starting */); // 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. mTargetRootTask.mDisplayContent.executeAppTransition(); } else { // If the target root-task was not previously focusable (previous top running // activity on that root-task was not visible) then any prior calls to move the // root-task to the will not update the focused root-task. If starting the new // activity now allows the task root-task to be focusable, then ensure that we // now update the focused root-task accordingly. if (mTargetRootTask.isTopActivityFocusable() && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) { if (!avoidMoveToFront()) { mTargetRootTask.moveToFront("startActivityInner"); } else { logPIOnlyCreatorAllowsBAL(); } } mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, mStartActivity, mOptions, mTransientLaunch); // 1 } } mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask); // Update the recent tasks list immediately when the activity starts mSupervisor.mRecentTasks.add(startedTask); mSupervisor.handleNonResizableTaskIfNeeded(startedTask, mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask); // If Activity's launching into PiP, move the mStartActivity immediately to pinned mode. // Note that mStartActivity and source should be in the same Task at this point. if (mOptions != null && mOptions.isLaunchIntoPip() && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask() && balVerdict.allows()) { mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity, sourceRecord, "launch-into-pip", null /* bounds */); } mSupervisor.getBackgroundActivityLaunchController() .onNewActivityLaunched(mStartActivity); // mStartActivity 是 ActivityStarter对象的ActivityRecord类型的成员变量 return START_SUCCESS; }
源码中
mStartActivity
是 ActivityStarter
对象的ActivityRecord
类型的成员变量.
mSupervisor
是ActivityTaskSupervisor
类型的对象,管理任务栈逻辑.
mRootWindowContainer
是RootWindowContainer
类型的对象.
我们看下RootWindowContainer
类型的对象的resumeFocusedTasksTopActivities
方法的源码:
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean resumeFocusedTasksTopActivities( Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions, boolean deferPause) { if (!mTaskSupervisor.readyToResume()) { return false; } boolean result = false; if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea() || getTopDisplayFocusedRootTask() == targetRootTask)) { result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions, deferPause); } for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent display = getChildAt(displayNdx); final boolean curResult = result; boolean[] resumedOnDisplay = new boolean[1]; final ActivityRecord topOfDisplay = display.topRunningActivity(); display.forAllRootTasks(rootTask -> { final ActivityRecord topRunningActivity = rootTask.topRunningActivity(); if (!rootTask.isFocusableAndVisible() || topRunningActivity == null) { return; } if (rootTask == targetRootTask) { // Simply update the result for targetRootTask because the targetRootTask // had already resumed in above. We don't want to resume it again, // especially in some cases, it would cause a second launch failure // if app process was dead. resumedOnDisplay[0] |= curResult; return; } if (topRunningActivity.isState(RESUMED) && topRunningActivity == topOfDisplay) { // Kick off any lingering app transitions form the MoveTaskToFront operation, // but only consider the top activity on that display. rootTask.executeAppTransition(targetOptions); } else { resumedOnDisplay[0] |= topRunningActivity.makeActiveIfNeeded(target); } }); result |= resumedOnDisplay[0]; if (!resumedOnDisplay[0]) { // In cases when there are no valid activities (e.g. device just booted or launcher // crashed) it's possible that nothing was resumed on a display. Requesting resume // of top activity in focused root task explicitly will make sure that at least home // activity is started and resumed, and no recursion occurs. final Task focusedRoot = display.getFocusedRootTask(); if (focusedRoot != null) { result |= focusedRoot.resumeTopActivityUncheckedLocked(target, targetOptions, false /* skipPause */); // 1 } else if (targetRootTask == null) { result |= resumeHomeActivity(null /* prev */, "no-focusable-task", display.getDefaultTaskDisplayArea()); } } } return result; }
focusedRoot
是Task
类型的对象,接下来查看方法resumeTopActivityUncheckedLocked
的源码:
frameworks/base/services/core/java/com/android/server/wm/Task.java
/** * Ensure that the top activity in the root task is resumed. * * @param prev The previously resumed activity, for when in the process * of pausing; can be null to call from elsewhere. * @param options Activity options. * @param deferPause When {@code true}, this will not pause back tasks. * * @return Returns true if something is being resumed, or false if * nothing happened. * * NOTE: It is not safe to call this method directly as it can cause an activity in a * non-focused root task to be resumed. * Use {@link RootWindowContainer#resumeFocusedTasksTopActivities} to resume the * right activity for the current system state. */ @GuardedBy("mService") boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options, boolean deferPause) { if (mInResumeTopActivity) { // Don't even start recursing. return false; } boolean someActivityResumed = false; try { // Protect against recursion. mInResumeTopActivity = true; if (isLeafTask()) { if (isFocusableAndVisible()) { someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause); // 1 } } else { int idx = mChildren.size() - 1; while (idx >= 0) { final Task child = (Task) getChildAt(idx--); if (!child.isTopActivityFocusable()) { continue; } if (child.getVisibility(null /* starting */) != TASK_FRAGMENT_VISIBILITY_VISIBLE) { if (child.topRunningActivity() == null) { // Skip the task if no running activity and continue resuming next task. continue; } // Otherwise, assuming everything behind this task should also be invisible. break; } someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options, deferPause); // Doing so in order to prevent IndexOOB since hierarchy might changes while // resuming activities, for example dismissing split-screen while starting // non-resizeable activity. if (idx >= mChildren.size()) { idx = mChildren.size() - 1; } } } // When resuming the top activity, it may be necessary to pause the top activity (for // example, returning to the lock screen. We suppress the normal pause logic in // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the // end. We call the {@link ActivityTaskSupervisor#checkReadyForSleepLocked} again here // to ensure any necessary pause logic occurs. In the case where the Activity will be // shown regardless of the lock screen, the call to // {@link ActivityTaskSupervisor#checkReadyForSleepLocked} is skipped. final ActivityRecord next = topRunningActivity(true /* focusableOnly */); if (next == null || !next.canTurnScreenOn()) { checkReadyForSleep(); } } finally { mInResumeTopActivity = false; } return someActivityResumed; }
继续看Task
对象的resumeTopActivityInnerLocked
方法:
@GuardedBy("mService") private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options, boolean deferPause) { if (!mAtmService.isBooting() && !mAtmService.isBooted()) { // Not ready yet! return false; } final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */); if (topActivity == null) { // There are no activities left in this task, let's look somewhere else. return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options); } final boolean[] resumed = new boolean[1]; final TaskFragment topFragment = topActivity.getTaskFragment(); resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause); // 1 forAllLeafTaskFragments(f -> { if (topFragment == f) { return; } if (!f.canBeResumed(null /* starting */)) { return; } resumed[0] |= f.resumeTopActivity(prev, options, deferPause); }, true); return resumed[0]; }
TaskFragment
的resumeTopActivity
源码如下:
frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, boolean skipPause) { ActivityRecord next = topRunningActivity(true /* focusableOnly */); if (next == null || !next.canResumeByCompat()) { return false; } next.delayedResume = false; if (!skipPause && !mRootWindowContainer.allPausedActivitiesComplete()) { // If we aren't skipping pause, then we have to wait for currently pausing activities. ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: some activity pausing."); return false; } final TaskDisplayArea taskDisplayArea = getDisplayArea(); // If the top activity is the resumed one, nothing to do. if (mResumedActivity == next && next.isState(RESUMED) && taskDisplayArea.allResumedActivitiesComplete()) { // Ensure the visibility gets updated before execute app transition. taskDisplayArea.ensureActivitiesVisible(null /* starting */, true /* notifyClients */); // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. executeAppTransition(options); // In a multi-resumed environment, like in a freeform device, the top // activity can be resumed, but it might not be the focused app. // Set focused app when top activity is resumed. However, we shouldn't do it for a // same task because it can break focused state. (e.g. activity embedding) if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null) { final ActivityRecord focusedApp = taskDisplayArea.mDisplayContent.mFocusedApp; if (focusedApp == null || focusedApp.getTask() != next.getTask()) { taskDisplayArea.mDisplayContent.setFocusedApp(next); } } ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity " + "resumed %s", next); return false; } // If we are sleeping, and there is no resumed activity, and the top activity is paused, // well that is the state we want. if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) { // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. executeAppTransition(options); ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and" + " all paused"); return false; } // Make sure that the user who owns this activity is started. If not, // we will just leave it as is because someone should be bringing // another user's activities to the top of the stack. if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) { Slog.w(TAG, "Skipping resume of top activity " + next + ": user " + next.mUserId + " is stopped"); return false; } // The activity may be waiting for stop, but that is no longer // appropriate for it. mTaskSupervisor.mStoppingActivities.remove(next); if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid); ActivityRecord lastResumed = null; final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask(); if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) { // So, why aren't we using prev here??? See the param comment on the method. prev // doesn't represent the last resumed activity. However, the last focus stack does if // it isn't null. lastResumed = lastFocusedRootTask.getTopResumedActivity(); } boolean pausing = !skipPause && taskDisplayArea.pauseBackTasks(next); if (mResumedActivity != null) { ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity); pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */, next, "resumeTopActivity"); } if (pausing) { ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to" + " start pausing"); // At this point we want to put the upcoming activity's process // at the top of the LRU list, since we know we will be needing it // very soon and it would be a waste to let it get killed if it // happens to be sitting towards the end. if (next.attachedToProcess()) { next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, true /* activityChange */, false /* updateOomAdj */, false /* addPendingTopUid */); } else if (!next.isProcessRunning()) { // Since the start-process is asynchronous, if we already know the process of next // activity isn't running, we can start the process earlier to save the time to wait // for the current activity to be paused. final boolean isTop = this == taskDisplayArea.getFocusedRootTask(); mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop, isTop ? HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY : HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY); } if (lastResumed != null) { lastResumed.setWillCloseOrEnterPip(true); } return true; } else if (mResumedActivity == next && next.isState(RESUMED) && taskDisplayArea.allResumedActivitiesComplete()) { // It is possible for the activity to be resumed when we paused back stacks above if the // next activity doesn't have to wait for pause to complete. // So, nothing else to-do except: // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. executeAppTransition(options); ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed " + "(dontWaitForPause) %s", next); return true; } // If the most recent activity was noHistory but was only stopped rather // than stopped+finished because the device went to sleep, we need to make // sure to finish it as we're making a new activity topmost. if (shouldSleepActivities()) { mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next); } if (prev != null && prev != next && next.nowVisible) { // The next activity is already visible, so hide the previous // activity's windows right now so we can show the new one ASAP. // We only do this if the previous is finishing, which should mean // it is on top of the one being resumed so hiding it quickly // is good. Otherwise, we want to do the normal route of allowing // the resumed activity to be shown so we can decide if the // previous should actually be hidden depending on whether the // new one is found to be full-screen or not. if (prev.finishing) { prev.setVisibility(false); if (DEBUG_SWITCH) { Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev + ", nowVisible=" + next.nowVisible); } } else { if (DEBUG_SWITCH) { Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev + ", nowVisible=" + next.nowVisible); } } } try { mTaskSupervisor.getActivityMetricsLogger() .notifyBeforePackageUnstopped(next.packageName); mAtmService.getPackageManagerInternalLocked().notifyComponentUsed( next.packageName, next.mUserId, next.packageName, next.toString()); /* TODO: Verify if correct userid */ } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " + next.packageName + ": " + e); } // We are starting up the next activity, so tell the window manager // that the previous one will be hidden soon. This way it can know // to ignore it when computing the desired screen orientation. boolean anim = true; final DisplayContent dc = taskDisplayArea.mDisplayContent; if (prev != null) { if (prev.finishing) { if (DEBUG_TRANSITION) { Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev); } if (mTaskSupervisor.mNoAnimActivities.contains(prev)) { anim = false; dc.prepareAppTransition(TRANSIT_NONE); } else { dc.prepareAppTransition(TRANSIT_CLOSE); } prev.setVisibility(false); } else { if (DEBUG_TRANSITION) { Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev); } if (mTaskSupervisor.mNoAnimActivities.contains(next)) { anim = false; dc.prepareAppTransition(TRANSIT_NONE); } else { dc.prepareAppTransition(TRANSIT_OPEN, next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0); } } } else { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous"); if (mTaskSupervisor.mNoAnimActivities.contains(next)) { anim = false; dc.prepareAppTransition(TRANSIT_NONE); } else { dc.prepareAppTransition(TRANSIT_OPEN); } } if (anim) { next.applyOptionsAnimation(); } else { next.abortAndClearOptionsAnimation(); } mTaskSupervisor.mNoAnimActivities.clear(); if (next.attachedToProcess()) { if (DEBUG_SWITCH) { Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.mAppStopped + " visibleRequested=" + next.isVisibleRequested()); } // If the previous activity is translucent, force a visibility update of // the next activity, so that it's added to WM's opening app list, and // transition animation can be set up properly. // For example, pressing Home button with a translucent activity in focus. // Launcher is already visible in this case. If we don't add it to opening // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation. final boolean lastActivityTranslucent = inMultiWindowMode() || mLastPausedActivity != null && !mLastPausedActivity.occludesParent(); // This activity is now becoming visible. if (!next.isVisibleRequested() || next.mAppStopped || lastActivityTranslucent) { next.app.addToPendingTop(); next.setVisibility(true); } // schedule launch ticks to collect information about slow apps. next.startLaunchTickingLocked(); ActivityRecord lastResumedActivity = lastFocusedRootTask == null ? null : lastFocusedRootTask.getTopResumedActivity(); final ActivityRecord.State lastState = next.getState(); mAtmService.updateCpuStats(); ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next); next.setState(RESUMED, "resumeTopActivity"); // Activity should also be visible if set mLaunchTaskBehind to true (see // ActivityRecord#shouldBeVisibleIgnoringKeyguard()). if (shouldBeVisible(next)) { // We have special rotation behavior when here is some active activity that // requests specific orientation or Keyguard is locked. Make sure all activity // visibilities are set correctly as well as the transition is updated if needed // to get the correct rotation behavior. Otherwise the following call to update // the orientation may cause incorrect configurations delivered to client as a // result of invisible window resize. // TODO: Remove this once visibilities are set correctly immediately when // starting an activity. final int originalRelaunchingCount = next.mPendingRelaunchCount; mRootWindowContainer.ensureVisibilityAndConfig(next, mDisplayContent, false /* deferResume */); if (next.mPendingRelaunchCount > originalRelaunchingCount) { // The activity is scheduled to relaunch, then ResumeActivityItem will be also // included (see ActivityRecord#relaunchActivityLocked) if it should resume. next.completeResumeLocked(); return true; } } try { final IApplicationThread appThread = next.app.getThread(); // Deliver all pending results. final ArrayList<ResultInfo> a = next.results; if (a != null) { final int size = a.size(); if (!next.finishing && size > 0) { if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); } final ActivityResultItem item = new ActivityResultItem(next.token, a); mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item); } } if (next.newIntents != null) { final NewIntentItem item = new NewIntentItem(next.token, next.newIntents, true /* resume */); mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item); } // Well the app will no longer be stopped. // Clear app token stopped state in window manager if needed. next.notifyAppResumed(); EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next), next.getTask().mTaskId, next.shortComponentName); mAtmService.getAppWarningsLocked().onResumeActivity(next); final int topProcessState = mAtmService.mTopProcessState; next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState); next.abortAndClearOptionsAnimation(); final ResumeActivityItem resumeActivityItem = new ResumeActivityItem( next.token, topProcessState, dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus()); mAtmService.getLifecycleManager().scheduleTransactionItem( appThread, resumeActivityItem); ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next); } catch (Exception e) { // Whoops, need to restart this activity! ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: " + "%s", lastState, next); 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 && lastFocusedRootTask != null && lastFocusedRootTask.isTopRootTaskInDisplayArea()) { next.showStartingWindow(false /* taskSwitch */); } mTaskSupervisor.startSpecificActivity(next, true, false); return true; } next.completeResumeLocked(); } else { // Whoops, need to restart this activity! if (!next.hasBeenLaunched) { next.hasBeenLaunched = true; } else { if (SHOW_APP_STARTING_PREVIEW) { next.showStartingWindow(false /* taskSwich */); } if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next); } ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next); mTaskSupervisor.startSpecificActivity(next, true, true); // 1 } return true; }
代码1处ActivityTaskSupervisor
对象调用startSpecificActivity
方法启动Activity
,源码如下:
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) { // Is this activity's application already running? final WindowProcessController wpc = mService.getProcessController(r.processName, r.info.applicationInfo.uid); boolean knownToBeDead = false; if (wpc != null && wpc.hasThread()) { try { realStartActivityLocked(r, wpc, andResume, checkConfig); // 1 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; // Remove the process record so it won't be considered as alive. mService.mProcessNames.remove(wpc.mName, wpc.mUid); mService.mProcessMap.remove(wpc.getPid()); } else if (ActivityTaskManagerService.isSdkSandboxActivityIntent( mService.mContext, r.intent)) { Slog.e(TAG, "Abort sandbox activity launching as no sandbox process to host it."); r.finishIfPossible("No sandbox process for the activity", false /* oomAdj */); r.launchFailed = true; r.detachFromProcess(); return; } r.notifyUnknownVisibilityLaunchedForKeyguardTransition(); final boolean isTop = andResume && r.isTopRunningActivity(); mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? HostingRecord.HOSTING_TYPE_TOP_ACTIVITY : HostingRecord.HOSTING_TYPE_ACTIVITY); }
代码1处ActivityTaskSupervisor
对象调用realStartActivityLocked
方法启动Activity
,源码如下:
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, boolean andResume, boolean checkConfig) throws RemoteException { if (!mRootWindowContainer.allPausedActivitiesComplete()) { // While there are activities pausing we skipping starting any new activities until // pauses are complete. NOTE: that we also do this for activities that are starting in // the paused state because they will first be resumed then paused on the client side. ProtoLog.v(WM_DEBUG_STATES, "realStartActivityLocked: Skipping start of r=%s some activities pausing...", r); return false; } final Task task = r.getTask(); if (andResume) { // Try pausing the existing resumed activity in the Task if any. if (task.pauseActivityIfNeeded(r, "realStart")) { return false; } final TaskFragment taskFragment = r.getTaskFragment(); if (taskFragment != null && taskFragment.getResumedActivity() != null) { if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, "realStart")) { return false; } } } final Task rootTask = task.getRootTask(); beginDeferResume(); // The LaunchActivityItem also contains process configuration, so the configuration change // from WindowProcessController#setProcess can be deferred. The major reason is that if // the activity has FixedRotationAdjustments, it needs to be applied with configuration. // In general, this reduces a binder transaction if process configuration is changed. proc.pauseConfigurationDispatch(); try { // schedule launch ticks to collect information about slow apps. r.startLaunchTickingLocked(); r.lastLaunchTime = SystemClock.uptimeMillis(); r.setProcess(proc); // Ensure activity is allowed to be resumed after process has set. if (andResume && !r.canResumeByCompat()) { andResume = false; } r.notifyUnknownVisibilityLaunchedForKeyguardTransition(); // Have the window manager re-evaluate the orientation of the screen based on the new // activity order. Note that as a result of this, it can call back into the activity // manager with a new orientation. We don't care about that, because the activity is // not currently running so we are just restarting it anyway. if (checkConfig) { // Deferring resume here because we're going to launch new activity shortly. // We don't want to perform a redundant launch of the same record while ensuring // configurations and trying to resume top activity of focused root task. mRootWindowContainer.ensureVisibilityAndConfig(r, r.mDisplayContent, true /* deferResume */); } if (mKeyguardController.checkKeyguardVisibility(r) && r.allowMoveToFront()) { // We only set the visibility to true if the activity is not being launched in // background, and is allowed to be visible based on keyguard state. This avoids // setting this into motion in window manager that is later cancelled due to later // calls to ensure visible activities that set visibility back to false. r.setVisibility(true); } final int applicationInfoUid = (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1; if ((r.mUserId != proc.mUserId) || (r.info.applicationInfo.uid != applicationInfoUid)) { Slog.wtf(TAG, "User ID for activity changing for " + r + " appInfo.uid=" + r.info.applicationInfo.uid + " info.ai.uid=" + applicationInfoUid + " old=" + r.app + " new=" + proc); } // Send the controller to client if the process is the first time to launch activity. // So the client can save binder transactions of getting the controller from activity // task manager service. final IActivityClientController activityClientController = proc.hasEverLaunchedActivity() ? null : mService.mActivityClientController; r.launchCount++; if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r); final LockTaskController lockTaskController = mService.getLockTaskController(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || (task.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED && lockTaskController.getLockTaskModeState() == LOCK_TASK_MODE_LOCKED)) { lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */); } try { if (!proc.hasThread()) { throw new RemoteException(); } List<ResultInfo> results = null; List<ReferrerIntent> newIntents = null; if (andResume) { // We don't need to deliver new intents and/or set results if activity is going // to pause immediately after launch. results = r.results; newIntents = r.newIntents; } if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Launching: " + r + " savedState=" + r.getSavedState() + " with results=" + results + " newIntents=" + newIntents + " andResume=" + andResume); EventLogTags.writeWmRestartActivity(r.mUserId, System.identityHashCode(r), task.mTaskId, r.shortComponentName); updateHomeProcessIfNeeded(r); mService.getPackageManagerInternalLocked().notifyPackageUse( r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY); mService.getAppWarningsLocked().onStartActivity(r); final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity(); final Configuration overrideConfig = r.getMergedOverrideConfiguration(); r.setLastReportedConfiguration(procConfig, overrideConfig); final ActivityWindowInfo activityWindowInfo = r.getActivityWindowInfo(); r.setLastReportedActivityWindowInfo(activityWindowInfo); logIfTransactionTooLarge(r.intent, r.getSavedState()); final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment(); if (organizedTaskFragment != null) { // Sending TaskFragmentInfo to client to ensure the info is updated before // the activity creation. mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent( organizedTaskFragment); } // Create activity launch transaction. final boolean isTransitionForward = r.isTransitionForward(); final IBinder fragmentToken = r.getTaskFragment().getFragmentToken(); final int deviceId = getDeviceIdForDisplayId(r.getDisplayId()); final LaunchActivityItem launchActivityItem = new LaunchActivityItem(r.token, r.intent, System.identityHashCode(r), r.info, procConfig, overrideConfig, deviceId, r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor, proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, r.takeSceneTransitionInfo(), isTransitionForward, proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController, r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken, r.initialCallerInfoAccessToken, activityWindowInfo); // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = new ResumeActivityItem(r.token, isTransitionForward, r.shouldSendCompatFakeFocus()); } else if (r.isVisibleRequested()) { lifecycleItem = new PauseActivityItem(r.token); } else { lifecycleItem = new StopActivityItem(r.token); } // Schedule transaction. if (shouldDispatchLaunchActivityItemIndependently(r.info.packageName, r.getUid())) { // LaunchActivityItem has @UnsupportedAppUsage usages. // Guard with targetSDK on Android 15+. // To not bundle the transaction, dispatch the pending before schedule new // transaction. mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread()); } mService.getLifecycleManager().scheduleTransactionAndLifecycleItems( proc.getThread(), launchActivityItem, lifecycleItem, // Immediately dispatch the transaction, so that if it fails, the server can // restart the process and retry now. true /* shouldDispatchImmediately */); // 1 if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) { // If the seq is increased, there should be something changed (e.g. registered // activity configuration). proc.setLastReportedConfiguration(procConfig); } if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 && mService.mHasHeavyWeightFeature) { // This may be a heavy-weight process! Note that the package manager will ensure // that only activity can run in the main process of the .apk, which is the only // thing that will be considered heavy-weight. if (proc.mName.equals(proc.mInfo.packageName)) { if (mService.mHeavyWeightProcess != null && mService.mHeavyWeightProcess != proc) { Slog.w(TAG, "Starting new heavy weight process " + proc + " when already running " + mService.mHeavyWeightProcess); } mService.setHeavyWeightProcess(r); } } } catch (RemoteException e) { if (r.launchFailed) { // This is the second time we failed -- finish activity and give up. Slog.e(TAG, "Second failure launching " + r.intent.getComponent().flattenToShortString() + ", giving up", e); proc.appDied("2nd-crash"); r.finishIfPossible("2nd-crash", false /* oomAdj */); return false; } // This is the first time we failed -- restart process and // retry. r.launchFailed = true; r.detachFromProcess(); throw e; } } finally { endDeferResume(); proc.resumeConfigurationDispatch(); } r.launchFailed = false; // TODO(lifecycler): Resume or pause requests are done as part of launch transaction, // so updating the state should be done accordingly. if (andResume && readyToResume()) { // As part of the process of launching, ActivityThread also performs // a resume. r.setState(RESUMED, "realStartActivityLocked"); r.completeResumeLocked(); } else if (r.isVisibleRequested()) { // This activity is not starting in the resumed state... which should look like we asked // it to pause+stop (but remain visible), and it has done so and reported back the // current icicle and other state. ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s " + "(starting in paused state)", r); r.setState(PAUSED, "realStartActivityLocked"); mRootWindowContainer.executeAppTransitionForAllDisplay(); } else { // This activity is starting while invisible, so it should be stopped. r.setState(STOPPING, "realStartActivityLocked"); } // Perform OOM scoring after the activity state is set, so the process can be updated with // the latest state. proc.onStartActivity(mService.mTopProcessState, r.info); // 2 // Launch the new version setup screen if needed. We do this -after- // launching the initial activity (that is, home), so that it can have // a chance to initialize itself while in the background, making the // switch back to it faster and look better. if (mRootWindowContainer.isTopDisplayFocusedRootTask(rootTask)) { mService.getActivityStartController().startSetupActivity(); // 3 } // Update any services we are bound to that might care about whether // their client may have activities. if (r.app != null) { r.app.updateServiceConnectionActivities(); } return true; }
在1处真正启动Activity
,
mService
是一个ActivityTaskManagerService
类型的对象。
mService.getLifecycleManager()
方法返回的是一个ClientLifecycleManager
类型的对象。
scheduleTransactionAndLifecycleItems
源码如下:
frameworks/base/services/core/java/com/android/server/wm/ClientLifecycleManager.java
void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem, @NonNull ActivityLifecycleItem lifecycleItem, boolean shouldDispatchImmediately) throws RemoteException { // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending // transactions at once. final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); clientTransaction.addTransactionItem(transactionItem); clientTransaction.addTransactionItem(lifecycleItem); onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately); }
scheduleTransactionAndLifecycleItems
方法又调用了onClientTransactionItemScheduled
方法,其源码如下:
frameworks/base/services/core/java/com/android/server/wm/ClientLifecycleManager.java
/** Must only be called with WM lock. */ private void onClientTransactionItemScheduled( @NonNull ClientTransaction clientTransaction, boolean shouldDispatchImmediately) throws RemoteException { if (shouldDispatchImmediately || shouldDispatchPendingTransactionsImmediately()) { // Dispatch the pending transaction immediately. mPendingTransactions.remove(clientTransaction.getClient().asBinder()); scheduleTransaction(clientTransaction); } } /** * Schedules a transaction, which may consist of multiple callbacks and a lifecycle request. * @param transaction A sequence of client transaction items. * @throws RemoteException * * @see ClientTransaction */ @VisibleForTesting void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException { final IApplicationThread client = transaction.getClient(); try { transaction.schedule(); // 1 } catch (RemoteException e) { Slog.w(TAG, "Failed to deliver transaction for " + client + "\ntransaction=" + transaction); throw e; } }
transaction
的ClientTransaction
的源码如下:
frameworks/base/core/java/android/app/servertransaction/ClientTransaction.java
/** * Schedule the transaction after it was initialized. It will be send to client and all its * individual parts will be applied in the following sequence: * 1. The client calls {@link #preExecute(ClientTransactionHandler)}, which triggers all work * that needs to be done before actually scheduling the transaction for callbacks and * lifecycle state request. * 2. The transaction message is scheduled. * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes * all callbacks and necessary lifecycle transitions. */ public void schedule() throws RemoteException { mClient.scheduleTransaction(this); }
mClient
是IApplicationThread
代理对象
frameworks/base/core/java/android/app/IApplicationThread.aidl
void scheduleTransaction(in ClientTransaction transaction);
则服务端进程调用链完成,现在回到App进程中。
ApplicationThread
是ActivityThread
的内部类,我们来看下scheduleTransaction
方法的源码。
frameworks/base/core/java/android/app/ActivityThread.java
/** * This manages the execution of the main thread in an * application process, scheduling and executing activities, * broadcasts, and other operations on it as the activity * manager requests. * * {@hide} */ public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal { private class ApplicationThread extends IApplicationThread.Stub{ @Override public void scheduleTransaction(ClientTransaction transaction) throws RemoteException { ActivityThread.this.scheduleTransaction(transaction); // 1 } } // 真正的实现方法 void sendMessage(int what, Object obj) { sendMessage(what, obj, 0, 0, false); } private void sendMessage(int what, Object obj, int arg1) { sendMessage(what, obj, arg1, 0, false); } private void sendMessage(int what, Object obj, int arg1, int arg2) { sendMessage(what, obj, arg1, arg2, false); } private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { if (DEBUG_MESSAGES) { Slog.v(TAG, "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj); } Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous(true); } mH.sendMessage(msg); // 2 } }
1处方法调用了ClientTransactionHandler
对象的scheduleTransaction
方法,其源码如下:
frameworks/base/core/java/android/app/ClientTransactionHandler.java
// Schedule phase related logic and handlers. /** Prepare and schedule transaction for execution. */ void scheduleTransaction(ClientTransaction transaction) { transaction.preExecute(this); sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction); }
如代码2处,
ClientTransactionHandler
对象的sendMessage
方法在ActivityThread
中实现,最终调用了,ActivityThread
的内部类H
类型的对象mH
的sendMessage
方法。我们来看一下EXECUTE_TRANSACTION
常量对应的逻辑源码:
frameworks/base/core/java/android/app/ActivityThread.java
case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; final ClientTransactionListenerController controller = ClientTransactionListenerController.getInstance(); controller.onClientTransactionStarted(); try { mTransactionExecutor.execute(transaction); // 1 } finally { controller.onClientTransactionFinished(); } break;
private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
由源码可知mTransactionExecutor
是一个TransactionExecutor
类型的对象,其execute
方法源码如下:
frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java
/** * Resolve transaction. * First all callbacks will be executed in the order they appear in the list. If a callback * requires a certain pre- or post-execution state, the client will be transitioned accordingly. * Then the client will cycle to the final lifecycle state if provided. Otherwise, it will * either remain in the initial state, or last state needed by a callback. */ public void execute(@NonNull ClientTransaction transaction) { if (DEBUG_RESOLVER) { Slog.d(TAG, tId(transaction) + "Start resolving transaction"); Slog.d(TAG, transactionToString(transaction, mTransactionHandler)); } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionExecuted"); try { executeTransactionItems(transaction); // 1 } catch (Exception e) { Slog.e(TAG, "Failed to execute the transaction: " + transactionToString(transaction, mTransactionHandler)); throw e; } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } mPendingActions.clear(); if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction"); } /** Cycles through all transaction items and execute them at proper times. */ @VisibleForTesting public void executeTransactionItems(@NonNull ClientTransaction transaction) { final List<ClientTransactionItem> items = transaction.getTransactionItems(); final int size = items.size(); for (int i = 0; i < size; i++) { final ClientTransactionItem item = items.get(i); if (item.isActivityLifecycleItem()) { executeLifecycleItem(transaction, (ActivityLifecycleItem) item); // 2 } else { executeNonLifecycleItem(transaction, item, shouldExcludeLastLifecycleState(items, i)); } } } private void executeLifecycleItem(@NonNull ClientTransaction transaction, @NonNull ActivityLifecycleItem lifecycleItem) { final IBinder token = lifecycleItem.getActivityToken(); final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); if (DEBUG_RESOLVER) { Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: " + lifecycleItem + " for activity: " + getShortActivityName(token, mTransactionHandler)); } if (r == null) { if (mTransactionHandler.getActivitiesToBeDestroyed().get(token) == lifecycleItem) { // Always cleanup for destroy item. lifecycleItem.postExecute(mTransactionHandler, mPendingActions); } // 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 */, transaction); // 3 // Execute the final transition with proper parameters. lifecycleItem.execute(mTransactionHandler, mPendingActions); lifecycleItem.postExecute(mTransactionHandler, mPendingActions); } /** * Transition the client between states with an option not to perform the last hop in the * sequence. This is used when resolving lifecycle state request, when the last transition must * be performed with some specific parameters. */ private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState, ClientTransaction transaction) { final int start = r.getLifecycleState(); if (DEBUG_RESOLVER) { Slog.d(TAG, tId(transaction) + "Cycle activity: " + getShortActivityName(r.token, mTransactionHandler) + " from: " + getStateName(start) + " to: " + getStateName(finish) + " excludeLastState: " + excludeLastState); } final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState); performLifecycleSequence(r, path, transaction); // 4 } /** Transition the client through previously initialized state sequence. */ private void performLifecycleSequence(ActivityClientRecord r, IntArray path, ClientTransaction transaction) { final int size = path.size(); for (int i = 0, state; i < size; i++) { state = path.get(i); if (DEBUG_RESOLVER) { Slog.d(TAG, tId(transaction) + "Transitioning activity: " + getShortActivityName(r.token, mTransactionHandler) + " to state: " + getStateName(state)); } switch (state) { case ON_CREATE: // 5 mTransactionHandler.handleLaunchActivity(r, mPendingActions, Context.DEVICE_ID_INVALID, null /* customIntent */); break; case ON_START: mTransactionHandler.handleStartActivity(r, mPendingActions, null /* sceneTransitionInfo */); break; case ON_RESUME: mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */, r.isForward, false /* shouldSendCompatFakeFocus */, "LIFECYCLER_RESUME_ACTIVITY"); break; case ON_PAUSE: mTransactionHandler.handlePauseActivity(r, false /* finished */, false /* userLeaving */, false /* autoEnteringPip */, mPendingActions, "LIFECYCLER_PAUSE_ACTIVITY"); break; case ON_STOP: mTransactionHandler.handleStopActivity(r, mPendingActions, false /* finalStateRequest */, "LIFECYCLER_STOP_ACTIVITY"); break; case ON_DESTROY: mTransactionHandler.handleDestroyActivity(r, false /* finishing */, false /* getNonConfigInstance */, "performLifecycleSequence. cycling to:" + path.get(size - 1)); break; case ON_RESTART: mTransactionHandler.performRestartActivity(r, false /* start */); break; default: throw new IllegalArgumentException("Unexpected lifecycle state: " + state); } } }
代码5处执行了mTransactionHandler.handleLaunchActivity()
方法,
mTransactionHandler
是lientTransactionHandler
类型的对象,其方法handleLaunchActivity
源码如下所示:
frameworks/base/core/java/android/app/ClientTransactionHandler.java
public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r, PendingTransactionActions pendingActions, int deviceId, Intent customIntent);
最终是由其子类ActivityThread
实现,其源码如下:
frameworks/base/core/java/android/app/ActivityThread.java
/** * Extended implementation of activity launch. Used when server requests a launch or relaunch. */ @Override public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, int deviceId, Intent customIntent) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); mSomeActivitiesChanged = true; if (r.profilerInfo != null) { mProfiler.setProfiler(r.profilerInfo); mProfiler.startProfiling(); } // Make sure we are running with the most recent config and resource paths. applyPendingApplicationInfoChanges(r.activityInfo.packageName); mConfigurationController.handleConfigurationChanged(null, null); updateDeviceIdForNonUIContexts(deviceId); if (localLOGV) Slog.v( TAG, "Handling launch of " + r); // Initialize before creating the activity if (ThreadedRenderer.sRendererEnabled && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { HardwareRenderer.preload(); } WindowManagerGlobal.initialize(); // Hint the GraphicsEnvironment that an activity is launching on the process. GraphicsEnvironment.hintActivityLaunch(); final Activity a = performLaunchActivity(r, customIntent); // 1 if (a != null) { r.createdConfig = new Configuration(mConfigurationController.getConfiguration()); reportSizeConfigurations(r); if (!r.activity.mFinished && pendingActions != null) { pendingActions.setOldState(r.state); pendingActions.setRestoreInstanceState(true); pendingActions.setCallOnPostCreate(true); } // Trigger ActivityWindowInfo callback if first launch or change from relaunch. handleActivityWindowInfoChanged(r); } else { // If there was an error, for any reason, tell the activity manager to stop us. ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED, null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } return a; } /** Core implementation of activity launch. */ private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; if (getInstrumentation() != null && getInstrumentation().getContext() != null && getInstrumentation().getContext().getApplicationInfo() != null && getInstrumentation().isSdkSandboxAllowedToStartActivities()) { // Activities launched from CTS-in-sandbox tests use a customized ApplicationInfo. See // also {@link SdkSandboxManagerLocal#getSdkSandboxApplicationInfoForInstrumentation}. r.packageInfo = getPackageInfo( getInstrumentation().getContext().getApplicationInfo(), mCompatibilityInfo, Context.CONTEXT_INCLUDE_CODE); } else if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo, Context.CONTEXT_INCLUDE_CODE); } 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); } boolean isSandboxActivityContext = sandboxActivitySdkBasedContext() && SdkSandboxActivityAuthority.isSdkSandboxActivityIntent( mSystemContext, r.intent); boolean isSandboxedSdkContextUsed = false; ContextImpl activityBaseContext; if (isSandboxActivityContext) { activityBaseContext = createBaseContextForSandboxActivity(r); if (activityBaseContext == null) { // Failed to retrieve the SDK based sandbox activity context, falling back to the // app based context. activityBaseContext = createBaseContextForActivity(r); } else { isSandboxedSdkContextUsed = true; } } else { activityBaseContext = createBaseContextForActivity(r); } Activity activity = null; try { java.lang.ClassLoader cl; if (isSandboxedSdkContextUsed) { // In case of sandbox activity, the context refers to the an SDK with no visibility // on the SandboxedActivity java class, the App context should be used instead. cl = activityBaseContext.getApplicationContext().getClassLoader(); } else { cl = activityBaseContext.getClassLoader(); } activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); // 2 StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo), activityBaseContext.getAttributionSource()); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir()); // updatePendingActivityConfiguration() reads from mActivities to update // ActivityClientRecord which runs in a different thread. Protect modifications to // mActivities to avoid race. synchronized (mResourcesManager) { mActivities.put(r.token, r); } if (activity != null) { CharSequence title = r.activityInfo.loadLabel(activityBaseContext.getPackageManager()); Configuration config = new Configuration(mConfigurationController.getCompatConfiguration()); if (r.overrideConfig != null) { config.updateFrom(r.overrideConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } // Activity resources must be initialized with the same loaders as the // application context. activityBaseContext.getResources().addLoaders( app.getResources().getLoaders().toArray(new ResourcesLoader[0])); activityBaseContext.setOuterContext(activity); activity.attach(activityBaseContext, 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.activityConfigCallback, r.assistToken, r.shareableActivityToken, r.initialCallerInfoAccessToken); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; checkAndBlockForNetworkAccess(); activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } if (r.mSceneTransitionInfo != null) { activity.mSceneTransitionInfo = r.mSceneTransitionInfo; r.mSceneTransitionInfo = null; } activity.mLaunchedFromBubble = r.mLaunchedFromBubble; activity.mCalled = false; // Assigning the activity to the record before calling onCreate() allows // ActivityThread#getActivity() lookup for the callbacks triggered from // ActivityLifecycleCallbacks#onActivityCreated() or // ActivityLifecycleCallback#onActivityPostCreated(). r.activity = activity; if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); // 1 } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.mLastReportedWindowingMode = config.windowConfiguration.getWindowingMode(); } r.setState(ON_CREATE); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; }
代码2处mInstrumentation
是一个Instrumentation
类型的对象,其调用了newActivity
方法,其源码如下:
frameworks/base/core/java/android/app/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 /* activityCallback */, null /* assistToken */, null /* shareableActivityToken */, null /* initialCallerInfoAccessToken */); return activity; }
代码1处调用了Activity
的onCreate
方法,
frameworks/base/core/java/android/app/Instrumentation.java
/** * Perform calling of an activity's {@link Activity#onCreate} * method. The default implementation simply calls through to that method. * * @param activity The activity being created. * @param icicle The previously frozen state (or null) to pass through to onCreate(). */ public void callActivityOnCreate(Activity activity, Bundle icicle) { prePerformCreate(activity); activity.performCreate(icicle); // 1 postPerformCreate(activity); }
1处代码最终调用了Activity
对象的onCreate
方法,其源码如下:
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) final void performCreate(Bundle icicle, PersistableBundle persistentState) { if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performCreate:" + mComponent.getClassName()); } dispatchActivityPreCreated(icicle); mCanEnterPictureInPicture = true; // initialize mIsInMultiWindowMode and mIsInPictureInPictureMode before onCreate final int windowingMode = getResources().getConfiguration().windowConfiguration .getWindowingMode(); mIsInMultiWindowMode = inMultiWindowMode(windowingMode); mIsInPictureInPictureMode = windowingMode == WINDOWING_MODE_PINNED; mShouldDockBigOverlays = getResources().getBoolean(R.bool.config_dockBigOverlayWindows); restoreHasCurrentPermissionRequest(icicle); final long startTime = SystemClock.uptimeMillis(); if (persistentState != null) { onCreate(icicle, persistentState); } else { onCreate(icicle); // 1 } final long duration = SystemClock.uptimeMillis() - startTime; EventLogTags.writeWmOnCreateCalled(mIdent, getComponentName().getClassName(), "performCreate", duration); mActivityTransitionState.readState(icicle); mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); mActivityTransitionState.setEnterSceneTransitionInfo(this, getSceneTransitionInfo()); dispatchActivityPostCreated(icicle); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); }
至此从startActivty
至Activity
对象的onCreate
方法执行的调用链全部分析完成。
有什么不足的地方还望大家批评指正,提出宝贵意见,我一定慢慢完善。😊
关联文章
// TODO
1. 启动一个未启动的App
进程的Activity
会发生什么?
2. 在LauncherActivity
界面点击应用图标会发生什么?
本文作者:爱情丶眨眼而去
本文链接:https://www.cnblogs.com/zshsboke/p/18679838/startActivity01
版权声明:本作品采用©️CC BY-NC-SA 4.0许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步