我们以Context的startActivity场景(option == null, FLAG_ACTIVITY_NEW_TASK)来梳理
整体时序图如下:
服务端
客户端
入口
ContextImpl的入口:
platform/frameworks/base/core/java/android/app/ContextImpl.java
@Override public void startActivity(Intent intent) { warnIfCallingFromSystemProcess(); startActivity(intent, null); } @Override public void startActivity(Intent intent, Bundle options) { if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity" + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }
通过Context来启动时,如果不是FLAG_ACTIVITY_NEW_TASK,并且option为null,会抛出异常。
下一步调用execStartActivity,关注这里传入的几个参数:
Context who:getOuterContext(),这里只ContextImp自己;如果是从Activity内启动,则是Activity对象自身(见Activity->startActivityForResult(...))。
IBinder contextThread:mMainThread.getApplicationThread(),也就是服务端向客户端通信时,客户端的binder stub
Activity target:null。也就是回退时的result Activity。
Intent intent:intent。
requestCode: -1。
options:null。
platform/frameworks/base/core/java/android/app/Instrumentation.java
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { try {intent.migrateExtraStreamToClipData(who); intent.prepareToLeaveProcess(who); int result = ActivityTaskManager.getService().startActivity(whoThread, who.getOpPackageName(), who.getAttributionTag(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); notifyStartActivityResult(result, options); checkStartActivityResult(result, intent); } catch (RemoteException e) {throw new RuntimeException("Failure from system", e); } return null; }
创建ActivityStarter
我们忽略过ActivityTaskManager从binder客户端到服务端的过程,直接看服务端代码:
platform/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@Override public final int startActivity(IApplicationThread caller, String callingPackage, String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId()); } @Override public int startActivityAsUser(IApplicationThread caller, String callingPackage, String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId, true /*validateIncomingUser*/); } private int startActivityAsUser(IApplicationThread caller, String callingPackage, @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions); assertPackageMatchesCallingUid(callingPackage); enforceNotIsolatedCaller("startActivityAsUser"); if (intent != null && intent.isSandboxActivity(mContext)) {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(); }
obtainStarter()方法实际是调用了mFactory.obtain(), 而mFactory为ActivityStarter的内部类DefaultFactory,看下DefaultFactory的obtain()。
platform/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
static class DefaultFactory implements Factory { private final int MAX_STARTER_COUNT = 3; private SynchronizedPool mStarterPool = new SynchronizedPool<>(MAX_STARTER_COUNT); public ActivityStarter obtain() { ActivityStarter starter = mStarterPool.acquire(); if (starter == null) { if (mService.mRootWindowContainer == null) { throw new IllegalStateException("Too early to start activity."); } starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor); } return starter; } }
obtain本质上是获取了一个ActivityStarter对象,用来启动activity。每个Intent对应一个ActivityStarter这里使用了SynchronizedPool--简单的对象池来管理对象的生产,对象池里有3个对象,如果申请时池子里没有可用对象,则直接new一个。
Activity参数整理、合法性检查与ActivityRecord的创建
现在,通过obtain()获得了一个ActivityStarter,通过执行其execute方法来启动一个Activity:
platform/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
int execute() { ... if (mRequest.activityInfo == null) { mRequest.resolveActivity(mSupervisor); } res = executeRequest(mRequest); ... } /** * 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(); // Reset the ActivityRecord#mCurrentLaunchCanTurnScreenOn state of last start activity in // case the state is not yet consumed during rapid activity launch. if (mLastStartActivityRecord != null) {mLastStartActivityRecord.setCurrentLaunchCanTurnScreenOn(false); } mLastStartActivityRecord = null; // *** 1. 整理参数,为后续构建ActivityRecorder等对象做准备 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. // Also the end of the line. err = ActivityManager.START_CLASS_NOT_FOUND; } 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 /* dataGrants */); } SafeActivityOptions.abort(options); return err; } // *** 2. 合法性检查 boolean abort; try { // 2.1权限检查 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 /* 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; @BalCode int balCode = BAL_ALLOW_DEFAULT; if (!abort) {try { // 2.2 是否允许后台启动 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "shouldAbortBackgroundActivityStart"); BackgroundActivityStartController balController = mController.getBackgroundActivityLaunchController(); balCode = balController.checkBackgroundActivityStart( callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, request.originatingPendingIntent, request.backgroundStartPrivileges, intent, checkedOptions); if (balCode != BAL_ALLOW_DEFAULT) {request.logMessage.append(" (").append( BackgroundActivityStartController.balCodeToString(balCode)) .append(")"); } } finally {Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } 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; } } // 2.3 拦截器拦截 mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage, callingFeatureId); if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid, callingUid, checkedOptions)) {// 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 /* 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; } // *** 3 是否要跳转到Review页面 // 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())); } } } // **** 4,对于Instant app(ephemeral app)启动ephemeral installer // 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; } } // *** 5. 创建ActivityRecord 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 (balCode != BAL_BLOCK && !isHomeProcess) {mService.resumeAppSwitches(); } mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession, request.voiceInteractor, startFlags, checkedOptions, inTask, inTaskFragment, balCode, intentGrants, realCallingUid); if (request.outActivity != null) {request.outActivity[0] = mLastStartActivityRecord; } return mLastStartActivityResult; }
executeRequest是个庞大的方法。做了几件事情:
1. 整理参数,为构建ActivityRecord做准备
其中
caller来自于之前传入的mMainThread.getApplicationThread()
一般场景中callingPid为-1,realCallingPid为Binder.getCallingPid()。 callingPid如果赋值,和realCallingPid一般是相同的,都来自Binder.getCallingPid(),但在使用PendingIntent的场景,callingPid代表创建了PendingIntent的进程,而realCallingPid代表通过使用PendingIntent.send()执行PendingIntent的进程。(见ActivityStarter.setCallingPid)
sourceRecord、resultRecord来自于resultTo要返回的Activity,也就是startActivityForResult要返回的Activity。
inTaskId:本场景中为空。一般在startActivityFromRecents等场景下,会设置。
2. 合法性检查
包括:
权限检查,比如目标Activity包名是否合法、是否为exported的Activity等通用权限
后台启动检查:通过BackgroundActivityStartController检查是否允许后台启动
拦截器检查:通过ActivityStartInterceptor检查各种拦截情况,如认为是潜在的恶意app(HarmfulApp)则改变intent为警告页面,到时不会跳转到原请求的activity。
3. 权限review(isPermissionsReviewRequired):如果需要在启动真正目标activity前跳转到权限review页面,则将原intent改为pending Intent传给review页面,待review结束后再继续启动。
4. 对于Instant app(ephemeral app)启动ephemeral installer
关于Instant app,详见https://developer.android.com/topic/google-play-instant
https://developer.android.com/topic/google-play-instant/overview
5. 创建ActivityRecored:final ActivityRecord r = new ActivityRecord.Builder(mService)
ActivityRecore的创建
在创建ActivityRecord时,ActivityRecord的构造函数里会设置setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);需要注意下:
private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent, ActivityOptions options, ActivityRecord sourceRecord) {int activityType = ACTIVITY_TYPE_UNDEFINED; if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord)) && isHomeIntent(intent) && !isResolverOrDelegateActivity()) {// This sure looks like a home activity! activityType = ACTIVITY_TYPE_HOME; if (info.resizeMode == RESIZE_MODE_FORCE_RESIZEABLE || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {// We only allow home activities to be resizeable if they explicitly requested it. info.resizeMode = RESIZE_MODE_UNRESIZEABLE; } } else if (mAtmService.getRecentTasks().isRecentsComponent(mActivityComponent, info.applicationInfo.uid)) {activityType = ACTIVITY_TYPE_RECENTS; } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT && canLaunchAssistActivity(launchedFromPackage)) {activityType = ACTIVITY_TYPE_ASSISTANT; } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_DREAM && mAtmService.canLaunchDreamActivity(launchedFromPackage) && DreamActivity.class.getName() == info.name) {activityType = ACTIVITY_TYPE_DREAM; } setActivityType(activityType); }
本场景中activityType为默认值ACTIVITY_TYPE_UNDEFINED
随后调用startActivityUnchecked,进而调用startActivityInner。
至此回顾下,目前整个时序进行到这里:
后续请见下篇。