1. 程式人生 > >Android9.0 Activity啟動流程分析(一)

Android9.0 Activity啟動流程分析(一)

1、ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay介紹

  本篇文章是基於Android refs/tags/android-9.0.0_r8分支的程式碼進行分析的
  在分析Activity啟動的原始碼之前先介紹一下Activity的一些基本概念。

1.1 Activity和Task

  關於Android中Activity和Task的介紹,個人覺得《深入理解Android》中的例子不錯。我們就借鑑其中的例子,進行相應的說明:
在這裡插入圖片描述
  上圖模擬了使用者在Android系統上想幹的三件事,分別用A、B、C表示。在Android中,每一件事可以被看作是一個Task;一個Task可以被細分成多個子步驟,每個子步驟可以被看作是一個Activity。

  另外A、B兩個Task使用了不同的Activity來完成相應的任務,即A、B兩個Task的Activity之間沒有複用。但是在Task C中,分別使用了Task A中的A1、Task B中的B2。這麼設計的原因是:使用者想做的事情(Task)即使完全不同,但是當細分Task為Activity時,就可能出現Activity功能類似的情況。當Task A和Task B中已經有能滿足需求的Activity時,Task C就會優先複用而不是重新建立Activity。通過重用Activity可以節省一定的開銷,同時為使用者提供一致的使用者介面。

  對Android的設計理念有一定的瞭解後,我們看看Android是如何組織Task及它所包含的Activity的。
在這裡插入圖片描述


  上圖為一個比較經典的示例:圖中的Task包含4個Activity。使用者可以單擊按鈕跳轉到下一個Activity。同時,通過返回鍵可以回到上一個Activity。圖中虛線下方為Activity的組織方式。從圖中可以看出,Task是以棧的形式管理Activity的,先啟動的Activity成位於棧底,後啟動的Activity將作為棧頂成員顯示在介面上。

1.2 ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay

  瞭解了Activity和Task的概念之後我們再看看AMS中的ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay

  1. ActivityRecord:在AMS中ActivityRecord是Activity的代表,ActivityRecord中的成員變數TaskRecord task代表當前Activity所在的棧,那麼他的棧資訊就儲存在TaskRecord中。
  2. TaskRecord:TaskRecord是Task的代表,TaskRecord其中兩個成員變數:
      ArrayList <ActivityRecord> mActivities:這個就是當前棧中所管理的Activity的列表
      ActivityStack mStack:他是ActivityRecord和TaskRecord的管理者
  3. ActivityStack:從名字上看,感覺他是真正的棧,但實際上他是一個Manager的角色,他是負責管理協調ActivityRecord和TaskRecord的。ActivityStack中的成員變數ArrayList<TaskRecord> mTaskHistory記錄的是當前ActivityStack管理的所有的棧。
  4. ActivityDisplay:他是顯示屏的抽象,每一個ActivityDisplay代表一塊螢幕,在開機過程中AMS會根據DisplayManager的Display資訊在AMS中建立ActivityDisplay。
    下面是一個簡單的關係圖

在這裡插入圖片描述
  可以看到一個ActivityDisplay可以包含多個ActivityStack,ActivityStack也包含很多個TaskRecord,一個TaskRecord又可以包含很多個ActivityRecord。

2、am命令

接下來我們將利用am命令啟動一個Activity,來分析Activity的啟動流程的原始碼。

am start -W -n com.android.settings/com.android.settings.Settings

-W:表示等待目標activity啟動的返回結果
-n :後接component name用於指定啟動的Activity

在shell中輸入如上命令之後會得到如下返回結果:

generic_x86_64:/ $ am start -W -n com.android.settings/com.android.settings.Settings
Starting: Intent { cmp=com.android.settings/.Settings }
Status: ok
Activity: com.android.settings/.Settings
ThisTime: 209
TotalTime: 209
WaitTime: 231
Complete

3、啟動流程分析

  下面講進入正篇,分析Activity啟動的原始碼

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
int runStartActivity(PrintWriter pw) throws RemoteException {
    // 首先通過makeIntent函式解析am命令的引數,然後根據引數生成Internet
    Intent intent;
    try {
        intent = makeIntent(UserHandle.USER_CURRENT);
    } catch (URISyntaxException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
	...

    // 建立並初始化ActivityOptions,ActivityOptions中儲存著DisplayId,WindowingMode,ActivityType等資訊
    ActivityOptions options = null;
    if (mDisplayId != INVALID_DISPLAY) {
        options = ActivityOptions.makeBasic();
        options.setLaunchDisplayId(mDisplayId);
    }
    if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
        if (options == null) {
            options = ActivityOptions.makeBasic();
        }
        options.setLaunchWindowingMode(mWindowingMode);
    }
    if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
        if (options == null) {
            options = ActivityOptions.makeBasic();
        }
        options.setLaunchActivityType(mActivityType);
    }
    if (mTaskId != INVALID_TASK_ID) {
        if (options == null) {
            options = ActivityOptions.makeBasic();
        }
        options.setLaunchTaskId(mTaskId);

        if (mIsTaskOverlay) {
            options.setTaskOverlay(true, true /* canResume */);
        }
    }
    if (mIsLockTask) {
        if (options == null) {
            options = ActivityOptions.makeBasic();
        }
        options.setLockTaskEnabled(true);
    }
    
    // 由於設定了-W引數,所以這裡mWaitOption = true
    if (mWaitOption) {
    	// mInterface為ActivityManagerService的代理物件,這裡呼叫了AMS的startActivityAndWait函式
        result = mInterface.startActivityAndWait(null, null, intent, mimeType,
              null, null, 0, mStartFlags, profilerInfo,
              options != null ? options.toBundle() : null, mUserId);
        res = result.result;
    } else {
        res = mInterface.startActivityAsUser(null, null, intent, mimeType,
              null, null, 0, mStartFlags, profilerInfo,
              options != null ? options.toBundle() : null, mUserId);
    } 
    // 處理result
    ...

    return 0;
}

  runStartActivity函式首先會解析am命令的引數並建立Intent和ActivityOptions,然後通過Binder呼叫ActivityManagerService的startActivityAndWait函式

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
    enforceNotIsolatedCaller("startActivityAndWait");
    userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
            userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
    WaitResult res = new WaitResult();
    // TODO: Switch to user app stacks here.
    // mActivityStartController.obtainStarter會返回ActivityStarter物件
    mActivityStartController.obtainStarter(intent, "startActivityAndWait")
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)
            .setStartFlags(startFlags)
            .setActivityOptions(bOptions)
            .setMayWait(userId)
            .setProfilerInfo(profilerInfo)
            .setWaitResult(res)
            .execute();
    return res;
}

  在startActivityAndWait函式中,首先獲取並初始化ActivityStarter物件,然後呼叫他的execute方法

// frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
int execute() {
    try {
        // TODO(b/64750076): Look into passing request directly to these methods to allow
        // for transactional diffs and preprocessing.
        if (mRequest.mayWait) {
            return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                    mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
                    mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                    mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                    mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                    mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                    mRequest.inTask, mRequest.reason,
                    mRequest.allowPendingRemoteAnimationRegistryLookup);
        } else {
            return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
                    mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
                    mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                    mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
                    mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
                    mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
                    mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
                    mRequest.outActivity, mRequest.inTask, mRequest.reason,
                    mRequest.allowPendingRemoteAnimationRegistryLookup);
        }
    } finally {
        onExecutionComplete();
    }
}

  這裡由於使用了-W引數所以這裡呼叫startActivityMayWait方法,前面都是一些轉呼叫,後面才開始真正的處理流程

3.1 建立ActivityRecord

// frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
private int startActivityMayWait(IApplicationThread caller, int callingUid,
        String callingPackage, Intent intent, String resolvedType,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode, int startFlags,
        ProfilerInfo profilerInfo, WaitResult outResult,
        Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
        int userId, TaskRecord inTask, String reason,
        boolean allowPendingRemoteAnimationRegistryLookup) {
    ...
	// 獲得呼叫者的uid和pid
    final int realCallingPid = Binder.getCallingPid();
    final int realCallingUid = Binder.getCallingUid();

    int callingPid;
    if (callingUid >= 0) {
        callingPid = -1;
    } else if (caller == null) {
        callingPid = realCallingPid;
        callingUid = realCallingUid;
    } else {
        callingPid = callingUid = -1;
    }

	// 儲存一個Intent的備份
    // 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);
    ...
    
    // 從PackageManagerService處獲取應用資訊,PackageManagerService會解析應用的AndroidManifest.xml檔案然後把應用資訊儲存
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
            0 /* matchFlags */,computeResolveFilterUid(
            callingUid, realCallingUid, mRequest.filterCallingUid));
       
    if (rInfo == null) {
       ...
    }
    
    // 根據ResolveInfo取得ActivityInfo,ActivityInfo儲存著Activity的基本資訊
    // Collect information about the target of the Intent.
    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

    synchronized (mService) {
    	// 判斷Configuration是否發生改變
        final ActivityStack stack = mSupervisor.mFocusedStack;
        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) {
            // heavy-weight process處理流程
            ... 
            
        }

        final ActivityRecord[] outRecord = new ActivityRecord[1];
        // 呼叫startActivity
        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);

        Binder.restoreCallingIdentity(origId);

        if (stack.mConfigWillChange) {
            // 如果Configuration發生改變則更新Configuration
            // 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.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                    "updateConfiguration()");
            stack.mConfigWillChange = false;
            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                    "Updating to new configuration after starting activity.");
            mService.updateConfigurationLocked(globalConfig, null, false);
        }

        if (outResult != null) {
       	   // 處理返回結果
           ...
		}
        mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
        return res;
    }
}

  這裡解釋下該函式中幾個重要引數:

引數 說明
caller 用於跟呼叫者程序的ApplicationThread進行通訊的binder代理類
callingUid 呼叫者的uid
callingPackage 呼叫者的包名
intent 啟動Activity時傳的引數
resultTo 根據resultTo可以找到呼叫者的ActivityRecord,用於接收返回結果
startFlags Intent攜帶的start activity對應的flag
profilerInfo 效能統計有關相關
outResult 用於儲存返回結果
globalConfig 以特定的配置啟動Activity,例如橫屏啟動Activity時
options ActivityOptions型別,可以指定Activity的WindowingMode,DisplayID等
userId 使用者ID,多使用者相關
inTask 指定啟動Activity的Task

  繼續分析啟動流程startActivity

// frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
private int startActivity(......) {

    if (TextUtils.isEmpty(reason)) {
        throw new IllegalArgumentException("Need to specify a reason.");
    }
    mLastStartReason = reason;
    mLastStartActivityTimeMs = System.currentTimeMillis();
    mLastStartActivityRecord[0] = null;
	// 轉呼叫
    mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
            aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
            callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
            options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
            inTask, allowPendingRemoteAnimationRegistryLookup);

    if (outActivity != null) {
        // mLastStartActivityRecord[0] is set in the call to startActivity above.
        outActivity[0] = mLastStartActivityRecord[0];
    }

    return getExternalResult(mLastStartActivityResult);
}

----------------------------------------------------------------------------------------------------------------------------------------------------------
private int startActivity(......) {
    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;

    ProcessRecord callerApp = null;
    if (caller != null) {
    // 如果引數中的呼叫者不為空,則從AMS中找到對應的ProcessRecord,目的是得到呼叫者的pid和uid
        callerApp = mService.getRecordForAppLocked(caller);
        if (callerApp != null) {
            callingPid = callerApp.pid;
            callingUid = callerApp.info.uid;
        } else {
            Slog.w(TAG, "Unable to find app for caller " + caller
                    + " (pid=" + callingPid + ") when starting: "
                    + intent.toString());
            err = ActivityManager.START_PERMISSION_DENIED;
        }
    }

    ...

    // sourceRecord用於儲存父Activity的資訊
    ActivityRecord sourceRecord = null;
    // resultRecord用於儲存接收啟動結果的Activity,對於startActivityForResult才有意義
    ActivityRecord resultRecord = null;
    if (resultTo != null) {
    	// 根據引數resultTo從ActivityStackSupervisor中獲取對應的ActivityRecord
        sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                "Will send result to " + resultTo + " " + sourceRecord);
        if (sourceRecord != null) {
            if (requestCode >= 0 && !sourceRecord.finishing) {
                resultRecord = sourceRecord;
            }
        }
    }
	
	// 得到啟動Activity使用的標誌位
    final int launchFlags = intent.getFlags();

    if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
	    // 處理launchFlags中包含Intent.FLAG_ACTIVITY_FORWARD_RESULT的情況
  		...
    }
	// 錯誤檢查,以及許可權檢查,這裡省略
    ...
    
	// 通過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 (mService.mDidAppSwitch) {
    	// 檢查呼叫程序是否有許可權切換Activity
        // This is the second allowed switch since we stopped switches,
        // so now just generally allow switches.  Use case: user presses
        // home (switches disabled, switch to home, mDidAppSwitch now true);
        // user taps a home icon (coming from home so allowed, we hit here
        // and now allow anyone to switch again).
        mService.mAppSwitchesAllowedTime = 0;
    }