從源碼的角度看Activity是如何啟動的

歡迎訪問我的個人博客,原文鏈接:http://wensibo.top/2017/07/05/Activity/ ,未經允許不得轉載!
大家好,今天想與大家一起分享的是Activity。我們平時接觸的最多的就是Activity了,作為四大組件中最為重要的老大,Activity究竟是如何啟動的呢?這篇文章將會從源碼的角度為大家進行全方位的解析,為了方便大家理解整個的過程,我會用流程圖的方式將整個過程串起來,希望對大家有所幫助。

開始吧!

一般我們啟動Activity有兩種方法,這里我就不再詳細說這兩種方法的用法了,不過他們都是調用了同樣的一個邏輯startActivity。所以我們分析Activity的啟動流程就從這個方法開始。

 public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        startActivityForResult(intent, -1);
    }
}

可以看到盡管startActivity()有多種重載方式,但是最終調用的還是startActivityForResult,所以我們只需要看startActivityForResult里面的實現邏輯即可。這里需要注意的一點就是調用了startActivityForResult方法時傳入的一個參數為-1,為什么是-1呢?還記得我們如果需要下一個Activity返回數據給目前這個Activity的時候都是調用startActivityForResult方法,不會去調用startActivity,因為startActivity盡管最后還是調用startActivityForResult,但是他設置了requestCode參數為-1,二在startActivityForResult方法中會判斷requestCode是否大于等于0,如果小于0就不會返回結果,因此我們都會選擇startActivityForResult方法以取回結果,并且設置其code參數大于等于0。下面我們來看看startActivityForResult的實現:

public void startActivityForResult(
@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {
        //mParent是一個Activity對象,表示該Activity是否由父Activity啟動
    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());
        }
        //這里就是判斷requestCode的邏輯
        if (requestCode >= 0) {
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

在startActivityForResult方法中,首先會判斷該Activity是否由父Activity啟動,即mParent,如果他是第一個Activity,就會調用Instrumentation的execStartActivity方法,這里再看一下判斷requestCode的邏輯:

if (requestCode >= 0) {
    mStartedActivity = true;
}

可以看到在這里確實判斷了requestCode的大小,大于等于0的時候才會返回結果,否則是不會的。
繼續說回execStartActivity方法,這里就是正真執行Activity啟動的操作,解釋一下他的幾個參數:

  • this,為啟動Activity的對象
  • mMainThread.getApplicationThread(),為Binder對象,是主進程的context對象
  • token,也是一個Binder對象,指向了服務端一個ActivityRecord對象
  • target,為啟動的Activity
  • intent,啟動的Intent對象
  • requestCode,請求碼
  • options,參數

這里的第一個Binder對象在我們的整個分析過程中將扮演者非常重要的作用,如果你對Binder不熟悉的話,請到這里了解有關Binder機制的內容。
接下來是execStartActivity方法:

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        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);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

這里最重要的就是調用了ActivityManagerNative.getDefault().startActivity(),但是ActivityManagerNative.getDefault()是什么東西呢?我們繼續看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");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};

可以看到其中聲明了一個Singleton封裝類,其類型是IActivityManager,注意到其中調用了asInterface方法,接著看他做了什么?

static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IActivityManager in =
        (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }

    return new ActivityManagerProxy(obj);
}


//ActivityManagerProxy:startActivity
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();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

可以看到asInterface返回了一個ActivityManagerProxy對象,也就是說ActivityManagerNative.getDefault()返回的就是一個ActivityManagerProxy對象,通過之前的BInder機制的文章我們可以知道Proxy是運行在客戶端的,客戶端通過將參數寫入Proxy類,接著Proxy就會通過Binder去遠程調用服務端的具體方法,因此,我們只是借用ActivityManagerProxy來調用ActivityManagerService的方法,他們之間的關系如下所示:

通信方式

到目前為止Activity的啟動流程就是如下所示了,可以看到Activity的啟動邏輯來到了AMS中。

在AMS中啟動Activity

@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
        resultWho, requestCode, startFlags, profilerInfo, options,
        UserHandle.getCallingUserId());
}

@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
    enforceNotIsolatedCaller("startActivity");
    userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
            false, ALLOW_FULL_ONLY, "startActivity", null);
    // TODO: Switch to user app stacks here.
    return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
            profilerInfo, null, null, options, false, userId, null, null);
}

在startActivity中直接調用了startActivityAsUser方法,而在startActivityAsUser中則是調用mStackSupervisor.startActivityMayWait方法:

final int startActivityLocked(IApplicationThread caller,
    Intent intent, String resolvedType, ActivityInfo aInfo,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    IBinder resultTo, String resultWho, int requestCode,
    int callingPid, int callingUid, String callingPackage,
    int realCallingPid, int realCallingUid, int startFlags, Bundle options,
    boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
    ActivityContainer container, TaskRecord inTask) {
    int err = ActivityManager.START_SUCCESS;

    ...
    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
        startFlags, true, options, inTask);

    ...
    return err;
    }

這個方法中主要構造了ActivityManagerService端的Activity對象:ActivityRecord,并根據Activity的啟動模式執行了相關邏輯。然后調用了startActivityUncheckedLocked方法,而在startActivityUncheckedLocked中則調用了startActivityUncheckedLocked方法,startActivityUncheckedLocked方法則會調用startActivityLocked方法,startActivityLocked又會調用resumeTopActivitiesLocked方法,其最后調用了resumeTopActivityLocked方法。
經過一系列的調用之后,最終來到了startPausingLocked方法,它會執行Activity的onPause方法,從而結束當前的Activity。
首先來看startPausingLocked方法:

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,boolean dontWait) {
    ...
    if (prev.app != null && prev.app.thread != null) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
        try {
            EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                    prev.userId, System.identityHashCode(prev),
                    prev.shortComponentName);
            mService.updateUsageStats(prev, false);
            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                    userLeaving, prev.configChangeFlags, dontWait);
        } catch (Exception e) {
            // Ignore exception, if process died other code will cleanup.
            Slog.w(TAG, "Exception thrown during pause", e);
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
    } else {
        mPausingActivity = null;
        mLastPausedActivity = null;
        mLastNoHistoryActivity = null;
    }
    ...
}

這里有一個很重要的地方就是prev.app.thread,其實他就是一個IApplicationThread類型的對象,而ApplicationThread則是ActivityThread的一個內部類,它繼承了IApplicationThread,并且都是Binder對象,所以說Appcation是一個客戶端,而ActivityThread中是一個服務端,到現在為止,Activity的調用來到了ActivityThread中,如下圖所示:

在ActivityThread中pause掉當前Activity

在ActivityThread中則是調用了schedulePauseActivity來執行pause操作:

public final void schedulePauseActivity(IBinder token, boolean finished,boolean userLeaving, int configChanges, boolean dontReport) {
    sendMessage(
        finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
        token,
        (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
        configChanges);
}

可以看到在schedulePauseActivity中則是通過handler來發送消息,消息類型為PAUSE_ACTIVITY_FINISHING,那接下來就應該看收到消息之后如何來處理了,

private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) {
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
        if (userLeaving) {
            performUserLeavingActivity(r);
        }

        r.activity.mConfigChangeFlags |= configChanges;
        performPauseActivity(token, finished, r.isPreHoneycomb());

        // Make sure any pending writes are now committed.
        if (r.isPreHoneycomb()) {
            QueuedWork.waitToFinish();
        }

        // Tell the activity manager we have paused.
        if (!dontReport) {
            try {
                ActivityManagerNative.getDefault().activityPaused(token);
            } catch (RemoteException ex) {
            }
        }
        mSomeActivitiesChanged = true;
    }
}

可以看到在方法內部通過調用performPauseActivity方法來實現對當前Activity的onPause生命周期方法的回調,具體是調用了performPause方法:

final void performPause() {
    mDoReportFullyDrawn = false;
    mFragments.dispatchPause();
    mCalled = false;
    onPause();
    mResumed = false;
    if (!mCalled && getApplicationInfo().targetSdkVersion
            >= android.os.Build.VERSION_CODES.GINGERBREAD) {
        throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPause()");
    }
    mResumed = false;
}

可以看到最終是調用了Activity的onPause()方法,接著我們回到handlePauseActivity的第二個方法ActivityManagerNative.getDefault().activityPaused(token),這是應用進程告訴服務進程,當前的Activity已經執行完成onPause方法了,其最后會調用completePauseLocked方法:

private void completePauseLocked(boolean resumeNext) {
    ...
    if (resumeNext) {
        final ActivityStack topStack = mStackSupervisor.getFocusedStack();
        if (!mService.isSleepingOrShuttingDown()) {
            mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
        } else {
            mStackSupervisor.checkReadyForSleepLocked();
            ActivityRecord top = topStack.topRunningActivityLocked(null);
            if (top == null || (prev != null && top != prev)) {
                // If there are no more activities available to run,
                // do resume anyway to start something.  Also if the top
                // activity on the stack is not the just paused activity,
                // we need to go ahead and resume it to ensure we complete
                // an in-flight app switch.
                mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
            }
        }
    }
}

可以看到經過了一系列的邏輯之后,又調用了resumeTopActivitiesLocked方法,而在該方法中則是調用了startSpecificActivityLocked 來啟動新的Activity。來看看目前的流程圖:


啟動新的Activity

在startSpecificActivityLocked方法中,其實現細節則是和調用Activity的pause方法一樣,都是通過Handler機制,發送一個啟動Activity的消息,接著處理該消息最后啟動Activity。其調用的是performLaunchActivity方法:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
        ...

        activity.mCalled = false;
        if (r.isPersistable()) {
           mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
           mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        ...
        if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
        ...
        return activity;
    }

可以看到最終的Activity對象終于創建出來了,可以看到其過程是使用反射機制創建的,而反射機制在Android系統中的應用也是隨處可見。在接下來的過程中還會繼續執行Activity的onCreate等一系列的生命周期方法。
最后再來看一下整個過程最終的流程圖:


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容