主頁 > 移動端開發 > Android Activity啟動程序分析

Android Activity啟動程序分析

2021-08-08 07:57:42 移動端開發

當我們在一個Activity中(或通過Context物件),呼叫startActivity()方法來啟動另一個Activity的程序中發生了什么呢?這就是本文想跟大家分享的內容,我們一起通過原始碼的方式來看下Activity的啟動程序,(本文是基于Android Q的原始碼)

?Activity的啟動流程大體分為兩步:找到要啟動的Activity;然后啟動它,(雖說有點像如何把大象裝進冰箱,但也確實是那么回事,只不過每個步驟有更多細節)

找到Activity

?要想啟動一個Activity,系統先要找到這個Activity的資訊,我們以從一個Activity中啟動另一個Activity為起點來分析Activity的啟動程序,在這個程序種會涉及到系統查找要啟動的Activity的程序,下面我們以時序圖來分析Activity的啟動程序:
在這里插入圖片描述
? 這只是Activity啟動程序種的一小部分,Activity的啟動的服務端是由ActivityTaskManagerService實作的,在ActivityTaskManagerService中Activity的啟動又是由ActivityStarter來實作,對于系統如何找到要啟動的Activity,大體分為兩步:

  • 得到ResolveInfo物件,實作方式是通過ActivityStackSuperVisor.resolveIntent()方法(最終是呼叫了PackageManagerService.resolveIntent()方法)決議而來;
  • 得到ActivityInfo物件,在得到ResolveInfo物件后,加上一些邏輯判斷得到ActivityInfo物件;至此系統就找到了需要啟動的Activity;

而這兩步都是在ActivityStarter.startActivityMayWait()方法中完成,

//ActivityStarter.java
private int startActivityMayWait(IApplicationThread caller, int callingUid,
        String callingPackage, int requestRealCallingPid, int requestRealCallingUid,
        Intent intent, String resolvedType, IVoiceInteractionSession voiceSession,
        IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, WaitResult outResult,
        Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
        int userId, TaskRecord inTask, String reason,
        boolean allowPendingRemoteAnimationRegistryLookup,
        PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
    // Refuse possible leaked file descriptors
    // 省略部分代碼....

    // Save a copy in case ephemeral needs it
    final Intent ephemeralIntent = new Intent(intent);
    // Don't modify the client's object!
    intent = new Intent(intent);
    if (componentSpecified
            && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
            && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
            && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
            && mService.getPackageManagerInternalLocked()
                    .isInstantAppInstallerComponent(intent.getComponent())) {
        intent.setComponent(null /*component*/);
        componentSpecified = false;
    }
    // 通過ActivitySuperVisor.resolveIntent()方法決議得到ResolveInfo物件;
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
            0 /* matchFlags */,
                    computeResolveFilterUid(
                            callingUid, realCallingUid, mRequest.filterCallingUid));
    if (rInfo == null) {
        UserInfo userInfo = mSupervisor.getUserInfo(userId);
        if (userInfo != null && userInfo.isManagedProfile()) {
            UserManager userManager = UserManager.get(mService.mContext);
            boolean profileLockedAndParentUnlockingOrUnlocked = false;
            long token = Binder.clearCallingIdentity();
            try {
                UserInfo parent = userManager.getProfileParent(userId);
                profileLockedAndParentUnlockingOrUnlocked = (parent != null)
                        && userManager.isUserUnlockingOrUnlocked(parent.id)
                        && !userManager.isUserUnlockingOrUnlocked(userId);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
            if (profileLockedAndParentUnlockingOrUnlocked) {
                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                        PackageManager.MATCH_DIRECT_BOOT_AWARE
                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                        computeResolveFilterUid(
                                callingUid, realCallingUid, mRequest.filterCallingUid));
            }
        }
    }
    //得到ActivityInfo物件
    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

    synchronized (mService.mGlobalLock) {
        final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
        stack.mConfigWillChange = globalConfig != null
                && mService.getGlobalConfiguration().diff(globalConfig) != 0;
        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                "Starting activity when config will change = " + stack.mConfigWillChange);

        final long origId = Binder.clearCallingIdentity();

        if (aInfo != null &&
                (aInfo.applicationInfo.privateFlags
                        & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
                mService.mHasHeavyWeightFeature) {
            // This may be a heavy-weight process!  Check to see if we already
            // have another, different heavy-weight process running.
            if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
                final WindowProcessController heavy = mService.mHeavyWeightProcess;
                if (heavy != null && (heavy.mInfo.uid != aInfo.applicationInfo.uid
                        || !heavy.mName.equals(aInfo.processName))) {
                    int appCallingUid = callingUid;
                    if (caller != null) {
                        WindowProcessController callerApp =
                                mService.getProcessController(caller);
                        if (callerApp != null) {
                            appCallingUid = callerApp.mInfo.uid;
                        } else {
                            Slog.w(TAG, "Unable to find app for caller " + caller
                                    + " (pid=" + callingPid + ") when starting: "
                                    + intent.toString());
                            SafeActivityOptions.abort(options);
                            return ActivityManager.START_PERMISSION_DENIED;
                        }
                    }

                    IIntentSender target = mService.getIntentSenderLocked(
                            ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                            appCallingUid, userId, null, null, 0, new Intent[] { intent },
                            new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                    | PendingIntent.FLAG_ONE_SHOT, null);

                    Intent newIntent = new Intent();
                    if (requestCode >= 0) {
                        // Caller is requesting a result.
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
                    }
                    newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
                            new IntentSender(target));
                    heavy.updateIntentForHeavyWeightActivity(newIntent);
                    newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                            aInfo.packageName);
                    newIntent.setFlags(intent.getFlags());
                    newIntent.setClassName("android",
                            HeavyWeightSwitcherActivity.class.getName());
                    intent = newIntent;
                    resolvedType = null;
                    caller = null;
                    callingUid = Binder.getCallingUid();
                    callingPid = Binder.getCallingPid();
                    componentSpecified = true;
                    rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
                            0 /* matchFlags */, computeResolveFilterUid(
                                    callingUid, realCallingUid, mRequest.filterCallingUid));
                    aInfo = rInfo != null ? rInfo.activityInfo : null;
                    if (aInfo != null) {
                        aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
                    }
                }
            }
        }

        final ActivityRecord[] outRecord = new ActivityRecord[1];
        int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
                voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
                allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
                allowBackgroundActivityStart);

        Binder.restoreCallingIdentity(origId);
        //省略部分代碼

        return res;
    }
}

啟動Activity

?在了解系統怎么找到要啟動的Activity后,我們現在就來看下啟動Activity的程序,在了解Activity啟動程序前,我們先了解下Activity 堆疊結構模型

Activity結構模型

下面這幅圖就是Activity(對應圖中的ActivityRecord)在歷史任務中的結構,
在這里插入圖片描述

資料結構描述

  • RootActivityContainer : Activity 容器的根節點
  • ActivityDisplay:每個實體代表系統中的一個顯示螢屏,也就是說如果你的Android設備是多屏的,那么將會有多個ActivityDisplay實體,
  • ActivityStack: Activity堆疊,用于表示管理堆疊中的Activity,
  • TaskRecord:ActivityRecord串列的封裝類,在ActivityStack中用于記錄歷史運行的Activity,
  • ActivityRecord:ActivityRecord表示歷史運行的Activity物件,
  • 通過dumpsys activity a命令可查看當前設備的activity堆疊結構,

啟動程序分析

對于上面的資料結構圖,要有個基本掌握,這有助于我們理解Activity啟動程序中的邏輯,我們先從ActivityStarter.startActivity()方法分析

//ActivityStarter.java
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
        String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
        String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
        SafeActivityOptions options,
        boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
        TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
        PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
    mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
    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 = ActivityManager.START_PERMISSION_DENIED;
        }
    }

    final int userId = aInfo != null && aInfo.applicationInfo != null
            ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;

    if (err == ActivityManager.START_SUCCESS) {
        Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
                + "} from uid " + callingUid);
    }

    ActivityRecord sourceRecord = null;
    //resultRecord用于記錄接收啟動Activity的結果(要記住這個方法是從Activity.startActivityForResult()呼叫下來的)
    ActivityRecord resultRecord = null;
    //這里的resultTo取決于是否是Activity(Activity中重寫了Context.startActivity()方法)物件啟動,
    //如果是Activity則resulTo為該Activity在Activity堆疊中的ActivityRecord實體,否則為null
    if (resultTo != null) {
        //如果在ActivityA 中啟動ActivityB,那么這個sourceRecord實際上就是ActivityA在Activity堆疊中的ActivityRecord實體;
        sourceRecord = mRootActivityContainer.isInAnyStack(resultTo);
        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                "Will send result to " + resultTo + " " + sourceRecord);
        if (sourceRecord != null) {
            if (requestCode >= 0 && !sourceRecord.finishing) {
                resultRecord = sourceRecord;
            }
        }
    }

    //......省略部分ActivityInfo和權限判斷代碼......
    
    //創建ActivityRecord物件
    ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
            callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
            resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
            mSupervisor, checkedOptions, sourceRecord);
    if (outActivity != null) {
        outActivity[0] = 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;
    }

    final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();

    // If we are starting an activity that is not from the same uid as the currently resumed
    // one, check whether app switches are allowed.
    if (voiceSession == null && (stack.getResumedActivity() == null
            || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
        if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                realCallingPid, realCallingUid, "Activity start")) {
            if (!(restrictedBgActivity && handleBackgroundActivityAbort(r))) {
                mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
                        sourceRecord, startFlags, stack, callerApp));
            }
            ActivityOptions.abort(checkedOptions);
            return ActivityManager.START_SWITCHES_CANCELED;
        }
    }

    mService.onStartActivitySetDidAppSwitch();
    mController.doPendingActivityLaunches(false);
    //呼叫另一個startActivity多載方法,該方法會呼叫startActivityUnchecked()方法,
    final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
            true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
    mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
    return res;
}

?在此方法中主要完成以下事情:

  • 要啟動的Activity包名類名是否存在以及權限的判斷;

  • 創建要啟動的Activity所對應的ActivityRecord物件

?接下來,我們看下startActivityUnchecked()方法,這個方法很長也很復雜,我們把Activity啟動分為以下幾種情況來分析:

  • 將Activity加入到現有的TaskRecord中,且Activity堆疊中已經有要啟動的Activity對應的ActivityRecord物件;

  • 將Activity加入到現有的TaskRecord中,但Activity堆疊中沒有要啟動的Activity對應的ActivityRecord物件;

  • 加入到新創建的TaskRecord中,

    我們先看第一種方式:

//ActivityStarter.java
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
        ActivityRecord[] outActivity, boolean restrictedBgActivity) {
    //重置變數并重新給mStartActivity,mIntent,mSourceRecord等變數賦值
    setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
            voiceInteractor, restrictedBgActivity);

    final int preferredWindowingMode = mLaunchParams.mWindowingMode;
    //計算得到LaunchFlag,并將結果賦值給mLaunchFlag; Flag定義見Intent.Flags注解
    computeLaunchingTaskFlags();
    //計算mSourceRecord所在的ActivityStack,并將得到的結果賦值給mSourceStack變數
    computeSourceStack();
    mIntent.setFlags(mLaunchFlags);
    //getReusableIntentActivity()方法主要是判斷要啟動的Activity對應的ActivityRecord物件是否可以放入已有的TaskRecord中,
    //如果可以則遍歷RootActivityContainer看要啟動的Activity對應的ActivityRecord是否已經存在,如果存在則回傳這個物件;
    //如果不存在則返要放入的TaskRecord中的頂層ActivityRecord
    ActivityRecord reusedActivity = getReusableIntentActivity();

    mSupervisor.getLaunchParamsController().calculate(
            reusedActivity != null ? reusedActivity.getTaskRecord() : mInTask,
            r.info.windowLayout, r, sourceRecord, options, PHASE_BOUNDS, mLaunchParams);
    mPreferredDisplayId =
            mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
                    : DEFAULT_DISPLAY;

    if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info,
            mPreferredDisplayId, true /* allowInstrumenting */)) {
        Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
        return START_CANCELED;
    }
    
    if (reusedActivity != null) {
        if (mService.getLockTaskController().isLockTaskModeViolation(
                reusedActivity.getTaskRecord(),
                (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
            Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }

        // True if we are clearing top and resetting of a standard (default) launch mode
        // ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
        // 如果launchMode為standard,且intent flag設定了FLAG_ACTIVITY_CLEAR_TOP或FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,
        // 則clearTopAndResetStandardLaunchMode為true
        final boolean clearTopAndResetStandardLaunchMode =
                (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
                        == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                && mLaunchMode == LAUNCH_MULTIPLE;

        // 如果目標ActivityRecord的TaskRecor為null,且clearTopAndResetStandardLaunchMode為true
        // 則設定目標ActivityRecord的TaskRecord.
        if (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) {
            mStartActivity.setTask(reusedActivity.getTaskRecord());
        }

        if (reusedActivity.getTaskRecord().intent == null) {
            // This task was started because of movement of the activity based on affinity...
            // Now that we are actually launching it, we can assign the base intent.
            reusedActivity.getTaskRecord().setIntent(mStartActivity);
        } else {
            final boolean taskOnHome =
                    (mStartActivity.intent.getFlags() & FLAG_ACTIVITY_TASK_ON_HOME) != 0;
            //判斷是否存在FLAG_ACTIVITY_TASK_ON_HOME標簽,如果存在且有FLAG_ACTIVITY_NEW_TASK標簽,
            //啟動這個Activity后會將Launcher所在的ActivityStack拉到當前所啟動的Activity所在ActivityStack后面,
            //舉例,應用A啟動應用B中的某個Activity,且設定的launchFlag包含FLAG_ACTIVITY_TASK_ON_HOME和FLAG_ACTIVITY_NEW_TASK;
            //當應用B退出的時候并不會回到應用A,而是回到Launcher應用,
            if (taskOnHome) {
                reusedActivity.getTaskRecord().intent.addFlags(FLAG_ACTIVITY_TASK_ON_HOME);
            } else {
                reusedActivity.getTaskRecord().intent.removeFlags(FLAG_ACTIVITY_TASK_ON_HOME);
            }
        }

        // This code path leads to delivering a new intent, we want to make sure we schedule it
        // as the first operation, in case the activity will be resumed as a result of later
        // operations.
        // 三種clear top的情況:1,launchFlag包含FLAG_ACTIVITY_CLEAR_TOP;2,launchFlag包含FLAG_ACTIVITY_NEW_DOCUMENT
        // 且不包含FLAG_ACTIVITY_MULTIPLE_TASK;3,launchMode為singleInstance或者singleTask
        if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                || isDocumentLaunchesIntoExisting(mLaunchFlags)
                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
            final TaskRecord task = reusedActivity.getTaskRecord();

            // In this situation we want to remove all activities from the task up to the one
            // being started. In most cases this means we are resetting the task to its initial
            // state.
            // 將要啟動的Activity所對應的ActivityRecord以上的物件從task中的ActivityRecord串列中移除
            final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
                    mLaunchFlags);

            // The above code can remove {@code reusedActivity} from the task, leading to the
            // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
            // task reference is needed in the call below to
            // {@link setTargetStackAndMoveToFrontIfNeeded}.
            if (reusedActivity.getTaskRecord() == null) {
                reusedActivity.setTask(task);
            }

            if (top != null) {
                if (top.frontOfTask) {
                    // Activity aliases may mean we use different intents for the top activity,
                    // so make sure the task now has the identity of the new intent.
                    top.getTaskRecord().setIntent(mStartActivity);
                }
                //如果堆疊頂的Activity就是需要啟動的Activity,則呼叫Activity的onNewIntent()方法
                deliverNewIntent(top);
            }
        }

        mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded
                (false /* forceSend */, reusedActivity);
        //setTargetStackAndMoveToFrontIfNeeded()和resumeTargetStackIfNeeded()方法的呼叫會將Activity堆疊中的
        //reusedActivity拉起并顯示到前臺,當然這個程序也會涉及到將前臺應用調到后臺,
        reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);

        final ActivityRecord outResult =
                outActivity != null && outActivity.length > 0 ? outActivity[0] : null;

        // When there is a reused activity and the current result is a trampoline activity,
        // set the reused activity as the result.
        if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
            outActivity[0] = reusedActivity;
        }

        if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
            // We don't need to start a new activity, and the client said not to do anything
            // if that is the case, so this is it!  And for paranoia, make sure we have
            // correctly resumed the top activity.
            resumeTargetStackIfNeeded();
            return START_RETURN_INTENT_TO_CALLER;
        }

        if (reusedActivity != null) {
            setTaskFromIntentActivity(reusedActivity);

            if (!mAddingToTask && mReuseTask == null) {
                // We didn't do anything...  but it was needed (a.k.a., client don't use that
                // intent!)  And for paranoia, make sure we have correctly resumed the top activity.
                resumeTargetStackIfNeeded();
                if (outActivity != null && outActivity.length > 0) {
                    // The reusedActivity could be finishing, for example of starting an
                    // activity with FLAG_ACTIVITY_CLEAR_TOP flag. In that case, return the
                    // top running activity in the task instead.
                    outActivity[0] = reusedActivity.finishing
                            ? reusedActivity.getTaskRecord().getTopActivity() : reusedActivity;
                }
                //這種情況是Activity已經被啟動過,ActivityRecord實體存在于TaskRecord中,
                //一種場景就是我們啟動一個應用的MainActivity后,按home鍵回到launcher后再進入這個應用,走的就是這個流程
                return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
            }
        }
    }
    //.....省略部分代碼.......
    
}

?注釋中已經做了部分說明,這里有個比較重要的方法getReusableIntentActivity(),這個方法是判斷要啟動的ActivityRecord是否加入到現有的TaskRecord中,這個判斷會涉及到launchMode、taskAffinity和launchFlag的設定,這里我們就不展開了,以后的文章我們再來詳細了解,這里我們先舉個例子說明:

<activity android:name="com.example.ltst.MainActivity"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="com.example.ltst.activity.ActivitySingleTaskCustomAffinity"
    android:taskAffinity=".singleTaskAffinity"
    android:launchMode="singleTask"/>
<activity android:name="com.example.ltst.activity.ActivitySingleTask"
    android:launchMode="singleTask">
</activity>
<activity android:name="com.example.ltst.activity.ActivitySingleInstance"
    android:launchMode="singleInstance"/>
<activity android:name="com.example.ltst.activity.ActivityStandard"/>

?如上所示,我們定義了五個Activity,現在從MainActivity中分別啟動另外四個Activity:

  • MainActivity > ActivitySingleTaskCustomAffinity, MainActivity的taskAffinity為默認的包名,ActivitySingleTaskCustomeAffinity自定義了taskAffinity,所以這種情況getReusableIntentActivity()回傳為null.
  • MainActivity > ActivitySingleTask,MainActivity和ActivitySingleTask的launchMode和taskAffinity(不顯示定義的話默認為應用包名)都是一樣的,這個時候getReusableIntentActivity()回傳的就是MainActivity的ActivityRecord物件;
  • MainActivity > ActivitySingleInstance,launchMode為singleInstance比較特殊,該模式下TaskRecord下只能有一個ActivityRecord,由于ActivitySingleInstance對應的TaskRecord還沒有被創建過,所以此時getReusableIntentActivity()回傳的也是null,
  • MainActivity > ActivityStandard, 這種情況getReusableIntentActivity回傳的是nul,這是因為getReusableIntentActivity()里面有判斷要啟動Activity的launchMode和launchFlag,如果launchFlag沒有設定FLAG_ACTIVITY_NEW_TASK且launchMode不為singleInstance和singleTask,則會回傳null,但這也不是意味著ActivityStandard會被放入其他的TaskRecord中,它還是會被加入到MainActivity所在的TaskRecord中,

對于已經被啟動過的Activity,且它對應的ActivityRecord還在ActivityStack中的時候,再次啟動該Activity的流程(也就是App熱啟動的情況)大概是這樣的:
在這里插入圖片描述

?在ActivityStack.resumeTopActivityInnerLocke()方法中會觸發一個ResumeActivityItem請求的事務,最后事務處理會執行ResumeActivityItem.excute()方法中的任務,ResumeActivityItem.excute()方法會呼叫ActivityThread.handleResuemActivity().

?如上面的例子中所述,對于創建新的TaskRecord有多種情況,我們來看另一種情況——App冷啟動,這種情況會涉及到ActivityStack和TaskRecord的創建,

//ActivityStarter.java
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
        ActivityRecord[] outActivity, boolean restrictedBgActivity) {
    //重置變數并重新給mStartActivity,mIntent,mSourceRecord等變數賦值
    setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
            voiceInteractor, restrictedBgActivity);

    //.......省略部分代碼....
    
    
    if (mStartActivity.packageName == null) {
        final ActivityStack sourceStack = mStartActivity.resultTo != null
                ? mStartActivity.resultTo.getActivityStack() : null;
        if (sourceStack != null) {
            sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
                    null /* data */);
        }
        ActivityOptions.abort(mOptions);
        return START_CLASS_NOT_FOUND;
    }

    // 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 ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
    final ActivityRecord topFocused = topStack.getTopActivity();
    final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
    final boolean dontStart = top != null && mStartActivity.resultTo == null
            && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
            && top.mUserId == mStartActivity.mUserId
            && top.attachedToProcess()
            && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
            || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
            // This allows home activity to automatically launch on secondary display when
            // display added, if home was the top activity on default display, instead of
            // sending new intent to the home activity on default display.
            && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
    if (dontStart) {
        // For paranoia, make sure we have correctly resumed the top activity.
        topStack.mLastPausedActivity = null;
        if (mDoResume) {
            mRootActivityContainer.resumeFocusedStacksTopActivities();
        }
        ActivityOptions.abort(mOptions);
        if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
            // We don't need to start a new activity, and the client said not to do
            // anything if that is the case, so this is it!
            return START_RETURN_INTENT_TO_CALLER;
        }

        deliverNewIntent(top);

        // Don't use mStartActivity.task to show the toast. We're not starting a new activity
        // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
        mSupervisor.handleNonResizableTaskIfNeeded(top.getTaskRecord(), preferredWindowingMode,
                mPreferredDisplayId, topStack);

        return START_DELIVERED_TO_TOP;
    }

    boolean newTask = false;
    final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
            ? mSourceRecord.getTaskRecord() : null;

    // Should this be considered a new task?
    int result = START_SUCCESS;
    // 下面的幾個判斷是計算要啟動的ActivityRecord所存放的ActivityStack,mTargetStack會被正確的賦值,
    if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
            && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {//冷啟動會進入該條件
        newTask = true;
        //setTaskFromReuseorCreateNewTask()會創建ActivityStack
        result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);
    } else if (mSourceRecord != null) {
        result = setTaskFromSourceRecord();
    } else if (mInTask != null) {
        result = setTaskFromInTask();
    } else {
        result = setTaskToCurrentTopOrCreateNewTask();
    }
    if (result != START_SUCCESS) {
        return result;
    }

    mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
            mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
    mService.getPackageManagerInternalLocked().grantEphemeralAccess(
            mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
            UserHandle.getAppId(mCallingUid));
    if (newTask) {
        EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
                mStartActivity.getTaskRecord().taskId);
    }
    ActivityStack.logStartActivity(
            EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
    mTargetStack.mLastPausedActivity = null;

    mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
            false /* forceSend */, mStartActivity);

    mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
            mOptions);
    if (mDoResume) {
        final ActivityRecord topTaskActivity =
                mStartActivity.getTaskRecord().topRunningActivityLocked();
        if (!mTargetStack.isFocusable()
                || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                && mStartActivity != topTaskActivity)) {
            mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
            mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
        } else {
            if (mTargetStack.isFocusable()
                    && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
                mTargetStack.moveToFront("startActivityUnchecked");
            }
            
            mRootActivityContainer.resumeFocusedStacksTopActivities(
                    mTargetStack, mStartActivity, mOptions);
        }
    } else if (mStartActivity != null) {
        mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord());
    }
    mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);

    mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(),
            preferredWindowingMode, mPreferredDisplayId, mTargetStack);

    return START_SUCCESS;
}

?冷啟動的情況下,setTaskFromReuseOrCreateNewTask會創建要啟動的Activity對應的ActivityRecord所在的Activitystack和TaskRecord,然后經過mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, mStartActivity, mOptions);實作啟動Activity的邏輯;RootActivityContainer.resumeFocusedStacksTopActivities()方法最侄訓呼叫ActivityStack.resumeTopActivityInnerLocked(), 啟動Activity的主要邏輯就在該方法中,這個方法比較長,我們只截取部分說明

@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    if (!mService.isBooting() && !mService.isBooted()) {
        // Not ready yet!
        return false;
    }
	if (DEBUG_STATES) Slog.v(TAG_STATES,
        "resumeTopActivityInnerLocked: ", new RuntimeException("resumeTopActivityInnerLocked").fillInStackTrace());

    // next即要啟動的Activity
    ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);

    final boolean hasRunningActivity = next != null;

    // TODO: Maybe this entire condition can get removed?
    if (hasRunningActivity && !isAttached()) {
        return false;
    }

    mRootActivityContainer.cancelInitializingActivities();


    boolean userLeaving = mStackSupervisor.mUserLeaving;
    mStackSupervisor.mUserLeaving = false;

    if (!hasRunningActivity) {
        // There are no activities left in the stack, let's look somewhere else.
        return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);
    }

    next.delayedResume = false;
    final ActivityDisplay display = getDisplay();

    // If the top activity is the resumed one, nothing to do.
    if (mResumedActivity == next && next.isState(RESUMED)
            && display.allResumedActivitiesComplete()) {
        // Make sure we have executed any pending transitions, since there
        // should be nothing left to do at this point.
        executeAppTransition(options);
        if (DEBUG_STATES) Slog.d(TAG_STATES,
                "resumeTopActivityLocked: Top activity resumed " + next);
        return false;
    }

    if (!next.canResumeByCompat()) {
        return false;
    }

    if (shouldSleepOrShutDownActivities()
            && mLastPausedActivity == next
            && mRootActivityContainer.allPausedActivitiesComplete()) {

        boolean nothingToResume = true;
        if (!mService.mShuttingDown) {
            final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
                    && next.canShowWhenLocked();
            final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
                    && next.mAppWindowToken != null
                    && next.mAppWindowToken.containsDismissKeyguardWindow();

            if (canShowWhenLocked || mayDismissKeyguard) {
                ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                        !PRESERVE_WINDOWS);
                nothingToResume = shouldSleepActivities();
            }
        }
        if (nothingToResume) {
            executeAppTransition(options);
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Going to sleep and all paused");
            return false;
        }
    }
    if (!mService.mAmInternal.hasStartedUserState(next.mUserId)) {
        Slog.w(TAG, "Skipping resume of top activity " + next
                + ": user " + next.mUserId + " is stopped");
        return false;
    }
    mStackSupervisor.mStoppingActivities.remove(next);
    mStackSupervisor.mGoingToSleepActivities.remove(next);
    next.sleeping = false;

    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);

    // If we are currently pausing an activity, then don't do anything until that is done.
    if (!mRootActivityContainer.allPausedActivitiesComplete()) {
        if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                "resumeTopActivityLocked: Skip resume: some activity pausing.");

        return false;
    }

    mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);

    boolean lastResumedCanPip = false;
    ActivityRecord lastResumed = null;
    final ActivityStack lastFocusedStack = display.getLastFocusedStack();
    if (lastFocusedStack != null && lastFocusedStack != this) {

        lastResumed = lastFocusedStack.mResumedActivity;
        if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {

            if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
                    + " next=" + next + " lastResumed=" + lastResumed);
            userLeaving = false;
        }
        lastResumedCanPip = lastResumed != null && lastResumed.checkEnterPictureInPictureState(
                "resumeTopActivity", userLeaving /* beforeStopping */);
    }

    // 如果Intent設定了FLAG_RESUME_WHILE_PAUSING 且上個Activity不以分屏的方式顯示,則resumeWhilePasuing為true
    final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
            && !lastResumedCanPip;
    // 在啟動Activity前先要讓堆疊中的Activity進入pause狀態,如果有Activity進入pause狀態則pausing為true
    boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
    if (mResumedActivity != null) {
        if (DEBUG_STATES) Slog.d(TAG_STATES,
                "resumeTopActivityLocked: Pausing " + mResumedActivity);
        pausing |= startPausingLocked(userLeaving, false, next, false);
    }
	//如果有Activity進入了pause狀態且resumeWhilePasuing為false,這個時候就會return,
    if (pausing && !resumeWhilePausing) {
        if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
                "resumeTopActivityLocked: Skip resume: need to start pausing");
        if (next.attachedToProcess()) {
            next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
                    true /* activityChange */, false /* updateOomAdj */);
        }
        if (lastResumed != null) {
            lastResumed.setWillCloseOrEnterPip(true);
        }
        return true;
    } else if (mResumedActivity == next && next.isState(RESUMED)
            && display.allResumedActivitiesComplete()) {
        executeAppTransition(options);
        if (DEBUG_STATES) Slog.d(TAG_STATES,
                "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
        return true;
    }

    //.....省略部分代碼......
}

?上面截取的是讓activity 堆疊中的Activity先進入pause狀態的邏輯,這也是為什么應用A啟動應用B的時候,應用A的Activity的onPause()方法會在應用B的Activity的onCreate()方法之前呼叫(這里沒有考慮分屏的情況),getDisplay().pauseBackStacks(userLeaving, next, false);會讓前臺App的Activity進入pause狀態,該方法會呼叫到ActivityStack.startPausingLocked()方法,在這個方法中主要的邏輯是發送一個PauseActivityItem的事務,

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
        ActivityRecord resuming, boolean pauseImmediately) {
    if (mPausingActivity != null) {
        Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
                + " state=" + mPausingActivity.getState());
        if (!shouldSleepActivities()) {
            completePauseLocked(false, resuming);
        }
    }
    ActivityRecord prev = mResumedActivity;

    if (prev == null) {
        if (resuming == null) {
            Slog.wtf(TAG, "Trying to pause when nothing is resumed");
            mRootActivityContainer.resumeFocusedStacksTopActivities();
        }
        return false;
    }

    if (prev == resuming) {
        Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
        return false;
    }

    if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
    else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
    mPausingActivity = prev;
    mLastPausedActivity = prev;
    mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
            || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
    prev.setState(PAUSING, "startPausingLocked");
    prev.getTaskRecord().touchActiveTime();
    clearLaunchTime(prev);

    mService.updateCpuStats();

    if (prev.attachedToProcess()) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
        try {
            EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
                    prev.shortComponentName, "userLeaving=" + userLeaving);

            mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
                    prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
                            prev.configChangeFlags, pauseImmediately));
        } catch (Exception e) {
            Slog.w(TAG, "Exception thrown during pause", e);
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
    } else {
        mPausingActivity = null;
        mLastPausedActivity = null;
        mLastNoHistoryActivity = null;
    }

    if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
        mStackSupervisor.acquireLaunchWakelock();
    }

    if (mPausingActivity != null) {
        if (!uiSleeping) {
            prev.pauseKeyDispatchingLocked();
        } else if (DEBUG_PAUSE) {
             Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
        }

        if (pauseImmediately) {
            completePauseLocked(false, resuming);
            return false;

        } else {
            schedulePauseTimeout(prev);
            return true;
        }

    } else {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
        if (resuming == null) {
            mRootActivityContainer.resumeFocusedStacksTopActivities();
        }
        return false;
    }
}

或許你有個疑問,既然這個地方直接return ture了,那ActivityStarter.startActivityUnchecked()呼叫是否就已經結束了呢;答案是肯定的,那你肯定又有疑問了,既然方法呼叫都結束了,那Activity到底是怎么啟動的呢,因為目前只講到了pause 之前應用的Activity,還沒講到啟動Activity,要回答這個問題,這就看下pause Activity是怎么實作的了,先放張時序圖,感興趣的同學去跟一跟代碼,

在這里插入圖片描述

?上面的時序圖主要分為兩個步驟:

  • 將前臺應用A退入后臺,實作的程序是在PauseActivityItem.excute()方法中呼叫ActivityThread.handlePauseActivity()方法;

  • 當應用A退入到后臺后,將應用B顯示到前臺,實作程序是PauseActivityItem.postExcute()中呼叫ActivityTaskManagerService.activityPaused(),然后經過一系列方法呼叫,最后執行到了ActivityStack.resumeTopActivityInnerLocked()方法,這個方法我們前面講過,只不過前面是在if (pausing && !resumeWhilePausing)條件下return 了;而這個時候下會呼叫ActivitySupervisor.startSpecificActivityLocked(),startSpecificActivityLocked()會呼叫realStartActivityLocked()方法,

我們先看下startSpecificActivityLocked()方法

//ActivityStackSupervisor.java
void startSpecificActivityLocked(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;
	//如果Activity所屬的行程已經創建,則啟動Activity
    if (wpc != null && wpc.hasThread()) {
        try {
            realStartActivityLocked(r, wpc, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }
        knownToBeDead = true;
    }
    if (getKeyguardController().isKeyguardLocked()) {
        r.notifyUnknownVisibilityLaunched();
    }

    try {
        if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
                    + r.processName);
        }
		//如果行程沒有創建,則創建行程,PooledLambda.obtainMessage回傳一個會呼叫
        //ActivityManagerInternal.startProcess(mService.mAmInternal, r.processName, r.info.applicationInfo, 
        //knownToBeDead, "activity", r.intent.getComponent())的Callback,
        final Message msg = PooledLambda.obtainMessage(
                ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
                r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
        mService.mH.sendMessage(msg);
    } finally {
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
}

?App冷啟動的程序會執行創建行程的程序,限于篇幅的原因,這里就不展開了,下次我們單獨講解,上面的時序圖也省略了行程創建的程序,同樣我們下次再補上,雖然這里省略了行程創建的程序,但是行程創建過后,也同樣會呼叫realStartActivtiyLocked()方法來繼續執行啟動Activity,我們看下realStartActivityLocked()個方法:

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
      boolean andResume, boolean checkConfig) throws RemoteException {

     //.....省略部分代碼......


          // Create activity launch transaction.
          final ClientTransaction clientTransaction = ClientTransaction.obtain(
                  proc.getThread(), r.appToken);

          final DisplayContent dc = r.getDisplay().mDisplayContent;
          clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                  System.identityHashCode(r), r.info,
                  // TODO: Have this take the merged configuration instead of separate global
                  // and override configs.
                  mergedConfiguration.getGlobalConfiguration(),
                  mergedConfiguration.getOverrideConfiguration(), r.compat,
                  r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                  r.icicle, r.persistentState, results, newIntents,
                  dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                          r.assistToken));

          // Set desired final state.
          final ActivityLifecycleItem lifecycleItem;
          if (andResume) {
              lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
          } else {
              lifecycleItem = PauseActivityItem.obtain();
          }
          clientTransaction.setLifecycleStateRequest(lifecycleItem);

          // Schedule transaction.
          mService.getLifecycleManager().scheduleTransaction(clientTransaction);

  //.....省略部分代碼......

  return true;
}

這個方法的核心是啟動一個設定了LaunchActivityItem回呼和ResumeActivityItem請求的事務,有了前面的經驗我們知道,最終LauncherActivityItem和ResumeActivityItem的execute()方法會被執行,我們看下他們的execute()方法實作:

//LaunchActivityItem.java
@Override
public void execute(ClientTransactionHandler client, IBinder token,
      PendingTransactionActions pendingActions) {
  Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
  ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
          mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
          mPendingResults, mPendingNewIntents, mIsForward,
          mProfilerInfo, client, mAssistToken);
  client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
  Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

//ResumeActivityItem.java
@Override
public void execute(ClientTransactionHandler client, IBinder token,
      PendingTransactionActions pendingActions) {
  Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
  client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
          "RESUME_ACTIVITY");
  Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

?方法很簡單,分別呼叫了ActivityThread.handleLauncheActivity()、ActivityThread.handleResumeActivity();這兩個方法最侄訓呼叫到Activity的onCreate()、onResume()方法,這個時候你可能就有疑問了,那onStart()方法呢,不是應該在onResume之前呼叫的嗎,這個就要從TransactionExecutor.execute()說起,我們看下這個方法

//TransactionExecutor.java

public void execute(ClientTransaction transaction) {
  if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");

  //.....省略部分代碼....
  //先執行callback,也就是會先執行LaunchActivityItem.execute()方法
  executeCallbacks(transaction);
  //然后呼叫executeLifecyleState()方法,這個方法會處理事務中的請求事件,
  executeLifecycleState(transaction);
  mPendingActions.clear();
  if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}

/** Transition to the final state if requested by the transaction. */
private void executeLifecycleState(ClientTransaction transaction) {
  final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
  if (lifecycleItem == null) {
      // No lifecycle request, return early.
      return;
  }

  final IBinder token = transaction.getActivityToken();
  final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
  if (DEBUG_RESOLVER) {
      Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
              + lifecycleItem + " for activity: "
              + getShortActivityName(token, mTransactionHandler));
  }

  if (r == null) {
      // Ignore requests for non-existent client records for now.
      return;
  }

  //在呼叫ActivityLifeCycleItem(ResumeActivityItem的父類)的execute()方法之前,先呼叫cycleToPath()方法;
  //lifecycleItem.getTargetState()即為ResumeActivityItem.getTargetState()回傳的值3(ON_RESUME).
  cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);

  // Execute the final transition with proper parameters.
  lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
  lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}

@VisibleForTesting
public void cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction) {
  cycleToPath(r, finish, false /* excludeLastState */, transaction);
}

private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,
      ClientTransaction transaction) {
  //經過execute()中執行executeCallbacks(transaction)后,Activity就處于ON_CTREATE狀態,這個時候start值為1(ON_CREATE),
  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);
  }
  //這個時候start = 1,finish = 3, excludeLastState = true;mHelper.getLifecyclePath方法會回傳只有一個元素(值為2)的整型陣列物件
  final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
  performLifecycleSequence(r, path, transaction);
}

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));
      }
      //這個時候state就是`mHelper.getLifecyclePath(start, finish, excludeLastState);`回傳的陣列中的元素值2(ON_START)
      //所以這時候會呼叫ActivityThread.handleStartActivity()方法,最終Activity.onStart()方法會被呼叫,
      switch (state) {
          //ON_CREATE、ON_START、ON_RESUEM、ON_PAUSE、ON_STOP、ON_DESTROY、ON_RESTART
          //定義在ActivityLifecycleItem.java中,他們的值分別為:0、1、2、3、4、5、6、7
          case ON_CREATE:
              mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                      null /* customIntent */);
              break;
          case ON_START:
              mTransactionHandler.handleStartActivity(r, mPendingActions);
              break;
          case ON_RESUME:
              mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
                      r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
              break;
          case ON_PAUSE:
              mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
                      false /* userLeaving */, 0 /* configChanges */, mPendingActions,
                      "LIFECYCLER_PAUSE_ACTIVITY");
              break;
          case ON_STOP:
              mTransactionHandler.handleStopActivity(r.token, false /* show */,
                      0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
                      "LIFECYCLER_STOP_ACTIVITY");
              break;
          case ON_DESTROY:
              mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
                      0 /* configChanges */, false /* getNonConfigInstance */,
                      "performLifecycleSequence. cycling to:" + path.get(size - 1));
              break;
          case ON_RESTART:
              mTransactionHandler.performRestartActivity(r.token, false /* start */);
              break;
          default:
              throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
      }
  }
}

?代碼中注釋已經做了說明了,總結下就是在呼叫ResumeActivityItem.execute()方法之前,會先執行cycleToPath()判斷Activity的起始狀態和目標狀態,如果后者大于前者,則需要執行兩者之間的狀態,至此冷啟動情況下啟動Activity就分析完了, 至于Activity啟動后顯示的程序可以參照下之前的文章《Activity UI顯示流程分析》,當然還有很多細節沒有展開,比如ActivityStack、TaskRecord的創建和更新,以及行程創建的程序;這寫以后有機會再分享,大家也可以自己研究,文中的時序圖可作為參考,

結束

?最后我們稍稍總結下,Activity啟動程序主要分為兩個程序:一是找到這個Activity(得到ActivityRecord物件);二是啟動它,而啟動程序又分為兩種情況:一種在Activity堆疊中有Activity對應的ActivityRecord物件(這個會跟launchMode和launch flag有關),這種情況會走Activity的onNewIntent()方法;另一種是Activity堆疊中沒有Activity對應的ActivityRecord,這種情況下又可分為多種子情況,但是我們不比糾結每種情況的細節;文中我們是分析了冷啟動一個App的情況,雖然行程創建程序沒有展開,但是后面Activity的啟動流程還是一致的,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/292373.html

標籤:其他

上一篇:監聽adapter里兩個按鈕

下一篇:python+appium自動化測驗如何控制App的啟動和退出

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more