圖解Activity啟動流程,進階高階

crazysunj發表於2017-07-17

前言

首先申明一下,覺得Activity用的賊6的,想求職面試的,想進階高階工程師的,想深入理解Activity的(感興趣)同學請往下看,不符合的沒關係,請收藏一下,想看了再點出來研究。
以下內容緊張吃雞,請繫好保險帶,我們要開車了。

— No picture,say a J8!

啟動流程

以下解析基於SDK25

Activity

到這裡,你是不是以為我會介紹一下Activity?錯,直接進入正題。

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}複製程式碼
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        startActivityForResult(intent, -1);
    }
}複製程式碼
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
    startActivityForResult(intent, requestCode, null);
}複製程式碼
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        if (requestCode >= 0) {
            mStartedActivity = true;
        }
        cancelInputsAndStartExitTransition(options);
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}複製程式碼

我們直接看mParent == null就行了,下面的已經是遠古級別了,已經被fragment淘汰,暫且不論。前者關鍵方法mInstrumentation.execStartActivity(),返回回撥結果。Instrumentation拿來測試過的同學並不陌生,這裡且當它是個黑科技工具。


進入execStartActivity方法。

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
    ...
    int result = ActivityManagerNative.getDefault()
        .startActivity(whoThread, who.getBasePackageName(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()),
                token, target != null ? target.mEmbeddedID : null,
                requestCode, 0, null, options);
    checkStartActivityResult(result, intent);
    ...
}複製程式碼

這裡解釋下前4個引數:

  • who:正在啟動該Activity的上下文
  • contextThread:正在啟動該Activity的上下文執行緒,這裡為ApplicationThread
  • token:正在啟動該Activity的標識
  • target:正在啟動該Activity的Activity,也就是回撥結果的Activity

我們先來看看下面的checkStartActivityResult方法。

public static void checkStartActivityResult(int res, Object intent) {
    if (res >= ActivityManager.START_SUCCESS) {
        return;
    }
    switch (res) {
        case ActivityManager.START_INTENT_NOT_RESOLVED:
        case ActivityManager.START_CLASS_NOT_FOUND:
            if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
                throw new ActivityNotFoundException(
                        "Unable to find explicit activity class "
                        + ((Intent)intent).getComponent().toShortString()
                        + "; have you declared this activity in your AndroidManifest.xml?");
            throw new ActivityNotFoundException(
                    "No Activity found to handle " + intent);
        case ActivityManager.START_PERMISSION_DENIED:
            throw new SecurityException("Not allowed to start activity "
                    + intent);
        case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
            throw new AndroidRuntimeException(
                    "FORWARD_RESULT_FLAG used while also requesting a result");
        case ActivityManager.START_NOT_ACTIVITY:
            throw new IllegalArgumentException(
                    "PendingIntent is not an activity");
        case ActivityManager.START_NOT_VOICE_COMPATIBLE:
            throw new SecurityException(
                    "Starting under voice control not allowed for: " + intent);
        case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
            throw new IllegalStateException(
                    "Session calling startVoiceActivity does not match active session");
        case ActivityManager.START_VOICE_HIDDEN_SESSION:
            throw new IllegalStateException(
                    "Cannot start voice activity on a hidden session");
        case ActivityManager.START_CANCELED:
            throw new AndroidRuntimeException("Activity could not be started for "
                    + intent);
        default:
            throw new AndroidRuntimeException("Unknown error code "
                    + res + " when starting " + intent);
    }
}複製程式碼

這些異常你都見過嗎?它就是根據返回碼和intent丟擲相應異常,最熟悉的就是activity沒有在AndroidManifest裡面註冊了。
這其實不是我們今天要講的關鍵,在這個方法中,我們還發現了熟悉的字眼startActivity,但呼叫者卻很陌生ActivityManagerNative.getDefault(),這到底是什麼玩意?

static public IActivityManager getDefault() {
    return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        IBinder b = ServiceManager.getService("activity");
        ...
        IActivityManager am = asInterface(b);
        ...
        return am;
    }
};
static public IActivityManager asInterface(IBinder obj) {
    ...
    return new ActivityManagerProxy(obj);
}複製程式碼

這樣看下來好像是ServiceManager構建了一個key為activity的物件,該物件作為ActivityManagerProxy的引數例項化建立單例並get返回。
這裡先不作解析,繼續連線上面的流程ActivityManagerNative.getDefault()
.startActivity(…)。我們已經知道startActivity方法其實是ActivityManagerProxy調的,我們再來看看。

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    ...
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    ...
    reply.recycle();
    data.recycle();
    return result;
}複製程式碼

如果對transact比較熟悉的話,那就很棒了,可以直接跳到下一節。不懂的同學繼續,這裡引入了Binder的概念,那麼Android為什麼要引入呢?這樣說來ActivityManagerProxy是不是個代理類啊?為什麼要引入,我不正面回答,假如我們要跨程式服務通訊,你是不是先建立一個xx.aidl檔案,然後會自動生成一個xx.java檔案。你有沒有仔細看過裡面的內容呢?沒看過的可以去看看。你會發現驚人的相似,如果是同一程式,我相信沒人會這麼做吧?從這些我們平時的開發經驗來看,這玩意貌似用在跨程式通訊上,就上面栗子來說,我們可以通過Service返回的Binder(其實就是個代理類)呼叫遠端的方法,我們的Activity相當於客戶端,而Service相當於服務端,這裡也是一樣,代理類就是ActivityManagerProxy。而真正傳輸資料的是mRemote.transact,簡單介紹下,第一個引數code是告訴服務端,我們請求的方法是什麼, 第二個引數data就是我們所傳遞的資料。這裡比較糾人的是為什麼要跨程式?這個你得了解下應用的啟動過程了,這裡我針對本篇文章簡單的科普一下,應用啟動會初始化一個init程式,而init程式會產生Zygote程式,從名字上看就是一個受精卵,它會fork很重要的SystemServer程式,比較著名的AMS,WMS,PMS都是這玩意啟動的,而我們的應用程式也是靠AMS通訊告訴Zygote程式fork出來的,因此需要跨程式通訊,順便解答下上面沒回答的問題,ServiceManager構建的就是我們傳說中的AMS。
花了比較長的篇幅介紹Binder,也是無奈之舉,幫助你們理解,但只是簡單的介紹,有些細節並沒有介紹,大家大可文外系統化地瞭解,很複雜,嘿嘿。

小總結:

ActivityManagerService

差不多該進入今天的主題了,我知道你想說MMP,但為了逼格,為了高薪,這又算得了什麼?


開搞,回顧上一節最後我們通過代理類ActivityManagerProxy呼叫了startActivity方法,讓我們來搜尋一下AMS的startActivity方法。仔細找找,好像是這個:

@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resulUserHandle.getCallingUserId());
}複製程式碼

誒?對比一下引數,哈哈差不多?那應該就是這個?引數什麼意思啊?如果你願意不如開啟AS找到Instrumentation類中execStartActivity方法,也就是我們上面所提到的,但很多細節省略了,我這裡簡單介紹一下,不然篇幅實在太長。

  • caller:就是我們的whoThread,見Instrumentation引數分析
  • callingPackage:包名
  • intent:意圖
  • resolvedType:就是我們AndroidManifest註冊時的MIME型別
  • resultTo:就是我們的token,見Instrumentation引數分析
  • resultWho:就是我們target的id
  • requestCode:這個就不說了
  • startFlags:啟動標誌,預設傳0
  • profilerInfo:預設傳null
  • options:略過

由於篇幅過長,接下來可能不會仔細分析引數,請大家順著引數的走向或者引數名理解。
AMS的startActivity方法什麼都沒做,直接調了startActivityAsUser方法,我們來看看這是什麼東西?

@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
    enforceNotIsolatedCaller("startActivity");
    userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),userId, false, ALLOW_FULL_ONLY, "startActivity", null);
    return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,profilerInfo, null, null, bOptions, false, userId, null, null);
}複製程式碼

其中enforceNotIsolatedCaller方法是檢查是否屬於被隔離的物件。mUserController.handleIncomingUser檢查操作許可權然後返回一個使用者id(涉及到Linux,有興趣的同學可以翻閱翻閱,這不是我們的重點),startActivityAsUser方法大概就是檢測下許可權,然後返回由mActivityStarter(ActivityStarter,可以這麼說,關於Activity啟動的所有資訊都在這了,然後根據資訊把Activity具體分配到哪個任務棧)呼叫的startActivityMayWait方法。


繼續跟蹤ActivityStarter的startActivityMayWait的方法。

final 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, IActivityManager.WaitResult outResult, Configuration config,
            Bundle bOptions, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask) {
    ...
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
    ...
    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
    ...
    final ProcessRecord heavy = mService.mHeavyWeightProcess;
    if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
            || !heavy.processName.equals(aInfo.processName))) {
        ...
    }
    ...
    int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
            aInfo, rInfo, voiceSession, voiceInteractor,
            resultTo, resultWho, requestCode, callingPid,
            callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
            options, ignoreTargetSecurity, componentSpecified, outRecord, container,
            inTask);
    ...
}複製程式碼

我的天,又多出了一個玩意,mmp,還能不能好好分析啦?mSupervisor(ActivityStackSupervisor)簡單來說就是用來管理ActivityStack的,後來新增的。resolveIntent和resolveActivity方法用來確定此次啟動activity資訊的。關於heavy(ResolveInfo)涉及到重量級程式(SystemServer, MediaServer,ServiceManager),如果當前系統中已經存在的重量級程式且不是即將要啟動的這個,那麼就要給Intent賦值。接下來繼續走我們的探索之路startActivityLocked。

final int startActivityLocked(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,
            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
            TaskRecord inTask) {
    ...
    ProcessRecord callerApp = null;
    ...
    ActivityRecord sourceRecord = null;
    ActivityRecord resultRecord = null;
    ...
    ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
            intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
            requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
            options, sourceRecord);
    ...
    err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
            true, options, inTask);
    ...
    return err;
}複製程式碼

又是一個賊JB長的方法,喝杯冰鎮可樂,繼續繼續。


callerApp ,sourceRecord,resultRecord是當中比較關鍵的變數,但都是為變數r服務,因為它記錄著整個方法雜七雜八的各種判斷結果,然後帶著變數r呼叫startActivityUnchecked方法,繼續跟進。

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
    setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
    voiceInteractor);
    computeLaunchingTaskFlags();
    ...
    mIntent.setFlags(mLaunchFlags);
    mReusedActivity = getReusableIntentActivity();
    ...
    boolean newTask = false;
    ...
     if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
        && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
        newTask = true;
        ...
    }
    ...
    mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
    ...
    mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                        mOptions);
    ...
}複製程式碼

這個方法相對重要,但還不是重頭戲,先看setInitialState,

private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
            boolean doResume, int startFlags, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
    reset();
    mStartActivity = r;
    mIntent = r.intent;
    mOptions = options;
    mCallingUid = r.launchedFromUid;
    mSourceRecord = sourceRecord;
    mVoiceSession = voiceSession;
    ...
    mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
    mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
    mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
    mLaunchFlags = adjustLaunchFlagsToDocumentMode(
            r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
     ...
}複製程式碼

這裡初始化了比較重要的mStartActivity,mIntent,mCallingUid,
mSourceRecord,mLaunchFlags。mLaunchFlags用來記錄我們Activity的啟動方式,省略部分都是根據啟動方式來初始化一堆變數或進行操作。下面的方法computeLaunchingTaskFlags還是用來初始化啟動標誌位的,我的天!真正初始化啟動方式標誌位後進行設定mIntent.setFlags(mLaunchFlags)。初始化啟動方式後,那麼我們需要初始化Tack了,如果對啟動模式有了解的同學下面的就比較好理解了,在
getReusableIntentActivity方法中,我們需要去找一個可複用的ActivityRecord,那麼只有啟動模式為SingleInstance才能真正的複用,因為整個系統就只有一個例項。這裡也列舉了相鄰地啟動同一個Activity的情況,為什麼有這種情況呢?感興趣的同學可以試試(主要還是考驗啟動模式的理解)。startActivityUnchecked方法細說真的是三天三夜都說不完。繼續看下面的方法吧。

final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
            ActivityOptions options) {
    ...
    mWindowManager.setAppVisibility(r.appToken, true);
    ...
}複製程式碼

臥槽,忍不住爆出了粗口,終於看見WMS,離出獄的日子不遠了,去tm的高薪,我寫寫xml就行了,要求這麼多。


mWindowManager.setAppVisibility(r.appToken, true);這句話表示這個Activity已經具備了顯示的條件。接下來我們長話短說,大體來看已經差不多了,通知後,我們會呼叫ActivityStackSupervisor的resumeFocusedStackTopActivityLocked方法,接著調ActivityStack的resumeTopActivityUncheckedLocked方法,再調自己的resumeTopActivityInnerLocked方法,如果Activity已存在需要可見狀態,那麼會調IApplicationThread的scheduleResumeActivity方法,且經過一系列判斷可見Activity;反之調ActivityStackSupervisor的startSpecificActivityLocked的方法,那麼繼續會調realStartActivityLocked的方法,看見這個方法的名字,我內心毫無波動,甚至想打人。

ActivityThread

終於調ApplicationThread的scheduleLaunchActivity方法啦!

@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
    updateProcessState(procState, false);
    ActivityClientRecord r = new ActivityClientRecord();
    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.referrer = referrer;
    r.voiceInteractor = voiceInteractor;
    r.activityInfo = info;
    r.compatInfo = compatInfo;
    r.state = state;
    r.persistentState = persistentState;
    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;
    r.startsNotResumed = notResumed;
    r.isForward = isForward;
    r.profilerInfo = profilerInfo;
    r.overrideConfig = overrideConfig;
    updatePendingConfiguration(curConfig);
    sendMessage(H.LAUNCH_ACTIVITY, r);
}複製程式碼

看最後,熟悉的味道,handler傳送message,那麼這個handler是什麼呢?有請天字第一號Handler,H。

private class H extends Handler {
    ...
    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                r.packageInfo = getPackageInfoNoCheck(
                        r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            ...
    }
}複製程式碼

我們直接看我們發的訊息,它呼叫了ActivityThread的handleLaunchActivity方法。

WindowManagerGlobal.initialize();//初始化WMS
Activity a = performLaunchActivity(r, customIntent);//建立並啟動複製程式碼
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ActivityInfo aInfo = r.activityInfo;//取出元件資訊,並一頓操作
    ...
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
         //開始建立Activity例項,通過類載入器建立,看引數就知道了
         ...
         Application app = r.packageInfo.makeApplication(false, mInstrumentation);
         //獲取Application
         ...
         activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);
         //與window建立關聯
         ...
         mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
         //callActivityOnCreate->activity.performCreate->onCreate,之後就很熟悉了
         ...
    }
}複製程式碼
public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }
    //如果mApplication不為空則直接返回,這也是為什麼Application為單例
    ...
    Application app = null;
    ...
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
    //建立Application,跟Activity一樣,都是用類載入器建立
    ...
    mApplication = app;//儲存下來
    ...
    instrumentation.callApplicationOnCreate(app);
    //callApplicationOnCreate->onCreate
    ...
}複製程式碼

小總結:

總結


誰吐槽我的作圖,我打誰。

結束語

到這裡,有一種高考結束的感覺。我要去坑一把王者農藥,不,雞吧,額,幾把。言歸正傳,今天的流程只是一種主流程,理想中的流程,實際有可能半路就已經結束了。雖然說AMS後期的分析,省略確實有點過分了。


原始碼分析是需要耐心的活,到這裡的同學,我相信都是認真看完一遍的,然後大家掏出AS,順著自己的理解再走一遍,不要看偷看我的文章哦,這樣效果更好,甚至被我省略的地方也能給你些許幫助,甚至比我理解的還透徹,If so,我是開心的!如果有什麼問題,可以發我郵箱,我會及時回覆。原始碼戳這裡,需要翻牆。

傳送門

Github:github.com/crazysunj
部落格:crazysunj.com/

相關文章