Android啟動(dòng)Activity源碼分析(五)

再遇Looper&構(gòu)造ActivityClientRecord

上一篇文章分析到:app.thread.scheduleLaunchActivity會(huì)跨進(jìn)程通知App進(jìn)程啟動(dòng)Activity估灿。thread是android.app.ActivityThread.ApplicationThread類(lèi)型的實(shí)例虾攻。
繼續(xù)分析android.app.ActivityThread.ApplicationThread#scheduleLaunchActivity:

        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
                Bundle state, List<ResultInfo> pendingResults,
                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profileFile = profileName;
            r.profileFd = profileFd;
            r.autoStopProfiler = autoStopProfiler;

            //系統(tǒng)信息相關(guān)內(nèi)容,場(chǎng)景無(wú)關(guān)
            updatePendingConfiguration(curConfig);

            queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
        }

參數(shù)挺多的胖秒,都是用于構(gòu)造ActivityClientRecord實(shí)例庐杨,前幾篇文章講過(guò)ActivityClientRecord是用來(lái)記錄App進(jìn)程側(cè)的Activity的斜棚,我們之前使用過(guò)MainActivity的ActivityClientRecord酱塔,而這次構(gòu)造的是屬于TargetActivity的ActivityClientRecord驮捍。

    private void queueOrSendMessage(int what, Object obj) {
        //是不是有點(diǎn)眼熟
        queueOrSendMessage(what, obj, 0, 0);
    }

是的,之前在pause掉MainActivity的時(shí)候就是調(diào)用的它玖翅,往looper里面拋信息的翼馆。這里思考一個(gè)問(wèn)題為什么要用looper去異步執(zhí)行?而不是直接順序同步執(zhí)行金度?
稍微思考一下即可得到答案:Binder是同步調(diào)用的应媚,也就是說(shuō)執(zhí)行到這AMS還在等待App進(jìn)程執(zhí)行完成呢,同樣startActivity最初的調(diào)用處還在等待AMS執(zhí)行完成呢猜极,如果不用looper異步執(zhí)行中姜,那么系統(tǒng)進(jìn)程會(huì)一直被掛在那里,這哪能行呢跟伏,AMS還要處理其他的startActivity或者其他的跨進(jìn)程調(diào)用呢丢胚。
因此當(dāng)系統(tǒng)進(jìn)程通知App進(jìn)程執(zhí)行任務(wù)時(shí),都是通過(guò)looper的方式來(lái)執(zhí)行的酬姆。就好比一個(gè)母豬十個(gè)崽嗜桌,奶就在那,已經(jīng)告訴你吃奶了辞色,愛(ài)吃不吃骨宠,還有剩下九個(gè)崽要管呢浮定。
android.app.ActivityThread.H#handleMessage:

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    ...
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    ...
                } break;
            ...
        }

onCreate、onStart层亿、onResume的準(zhǔn)備工作

繼續(xù)分析android.app.ActivityThread#getPackageInfoNoCheck:


    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
            CompatibilityInfo compatInfo) {
        return getPackageInfo(ai, compatInfo, null, false, true);
    }
    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
        synchronized (mPackages) {
            WeakReference<LoadedApk> ref;
            //先從內(nèi)存緩存里取Apk信息
            if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }
            LoadedApk packageInfo = ref != null ? ref.get() : null;
            //取不到的話(huà)創(chuàng)建一個(gè)桦卒,并且放在內(nèi)存緩存里,下次可以直接用
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                ...
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
                if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }

具體如何執(zhí)行不再分析匿又,該函數(shù)就是獲得了Apk的信息方灾。賦值給了r.packageInfo。
android.app.ActivityThread#handleLaunchActivity:

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward);
            ...
        } else {
            ...
        }
    }

onCreate碌更、onStart的調(diào)用

函數(shù)代碼比較長(zhǎng)裕偿,實(shí)際上場(chǎng)景相關(guān)的只有兩行,分別是performLaunchActivity和handleResumeActivity的調(diào)用痛单。
先分析android.app.ActivityThread#performLaunchActivity嘿棘。
函數(shù)比較長(zhǎng)(難道google編碼規(guī)范,沒(méi)有限制函數(shù)行數(shù)嗎旭绒,100多行了)鸟妙,分兩段進(jìn)行分析,第一段:


    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        //若沒(méi)有拿到Apk信息挥吵,再次嘗試取拿重父,不走這,之前已經(jīng)拿到了Apk信息忽匈,上文分析過(guò)
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        //當(dāng)前場(chǎng)景的組件名不會(huì)為null房午。若是null,還會(huì)嘗試向PMS查找
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }
        ...

        Activity activity = null;
        try {
            //拿到ClassLoader
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            //利用ClassLoader和組件名(TargetActivity類(lèi)全稱(chēng))脉幢,實(shí)例化TargetActivity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            //不知道干啥用的歪沃,場(chǎng)景無(wú)關(guān)
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            //一些賦值
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            ...
        }
        //第一段結(jié)束
        ...
    }

第一段核心內(nèi)容:創(chuàng)建Activity實(shí)例嗦锐。
android.app.Instrumentation#newActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent):

    public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

創(chuàng)建過(guò)程很簡(jiǎn)單嫌松。
繼續(xù)分析系第二段:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        //第二段開(kāi)始
        try {
            //拿到App進(jìn)程對(duì)應(yīng)的Application對(duì)象
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...

            if (activity != null) {
                //設(shè)置Activity相關(guān)的Context內(nèi)容
                ContextImpl appContext = new ContextImpl();
                appContext.init(r.packageInfo, r.token, this);
                appContext.setOuterContext(activity);
                ...
                //把一些信息設(shè)置個(gè)Activity,其中Activity的”身份證”mToken就是在這里設(shè)置的
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config);
                ...
                activity.mCalled = false;
                //調(diào)用onCreate
                mInstrumentation.callActivityOnCreate(activity, r.state);
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                //把Activity記錄給ActivityClientRecord
                r.activity = activity;
                //還沒(méi)有resume奕污,先設(shè)置成true
                r.stopped = true;
                //onCreate調(diào)用后萎羔,馬上調(diào)用onStart
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                ...
                
            }
            //還沒(méi)有resume,先設(shè)置成true
            r.paused = true;
            
            //把ActivityClientRecord記錄下來(lái)碳默。前面文章中使用過(guò)mActivities查找MainActivity的ActivityClientRecord
            mActivities.put(r.token, r);

        } 
        ...

        return activity;
    }

這函數(shù)主要干了一下幾件事:

  1. 創(chuàng)建Activity實(shí)例
  2. 給Activity設(shè)置好Context
  3. 設(shè)置attach一些Activity需要的信息
  4. 調(diào)用Activity的onCreate方法
  5. 調(diào)用Activity的onStart方法
  6. 把Activity實(shí)例記錄下來(lái)贾陷,方便后續(xù)查找
    這里再簡(jiǎn)單分析下onCreate的調(diào)用android.app.Instrumentation#callActivityOnCreate:
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        ...
        activity.performCreate(icicle);
        ...
    }
    final void performCreate(Bundle icicle) {
        onCreate(icicle);
        ...
        //Fragment的onActivityCreated分發(fā)
        mFragments.dispatchActivityCreated();
    }

調(diào)用和onPause類(lèi)似,也是用過(guò)Instrumentation進(jìn)行調(diào)用嘱根,也會(huì)驗(yàn)證是否調(diào)用了super方法髓废。不再贅述。
在onCreate里會(huì)調(diào)用setContentView來(lái)設(shè)置布局该抒。
android.app.Activity#setContentView(int):

    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initActionBar();
    }

getWindow()返回的是com.android.internal.policy.impl.PhoneWindow類(lèi)型的慌洪。

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            // 構(gòu)造DecorView
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        // 根據(jù)xml創(chuàng)建View樹(shù)
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

setContentView構(gòu)造DecorView和View樹(shù),這里不是本文重點(diǎn),不再分析冈爹。

onResume的調(diào)用

繼續(xù)回到handleLaunchActivity中涌攻,分析:
android.app.ActivityThread#handleResumeActivity:

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
        ...
        // 執(zhí)行onResume
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);
    
            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                ...
            }
            //記錄Window、DecorView等频伤。最后把View加進(jìn)Window(使用ViewRootImpl進(jìn)行add)
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                //暫時(shí)設(shè)置成INVISIBLE
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
            } else if (!willBeVisible) {
                ...
            }
            ...
                    // 里面會(huì)把DecorView設(shè)置成VISIBLE
                    r.activity.makeVisible();
    }

代碼比較長(zhǎng)恳谎,但場(chǎng)景有用的就兩步:

  1. 調(diào)用onResume函數(shù)
  2. 記錄Window、DecorView等到ActivityClientRecord里憋肖,并把View添加進(jìn)Window

下面我們繼續(xù)分析android.app.ActivityThread#performResumeActivity:

    public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        //根據(jù)token拿ActivityClientRecord因痛,老套路了
        ActivityClientRecord r = mActivities.get(token);
        ...
        //顯而易見(jiàn),走這兒
        if (r != null && !r.activity.mFinished) {
            //clearHide傳入的是true岸更,設(shè)置了一些變量婚肆,一時(shí)半會(huì)兒用不到
            if (clearHide) {
                r.hideForNow = false;
                r.activity.mStartedActivity = false;
            }
            try {
                ...
                //里面用調(diào)用onResume
                r.activity.performResume();
                ...

                //這倆在調(diào)用onCreate的時(shí)候都設(shè)置成true了,現(xiàn)在已經(jīng)resume了坐慰,設(shè)置成false
                r.paused = false;
                r.stopped = false;
                r.state = null;
            } catch (Exception e) {
                ...
            }
        }
        return r;
    }

過(guò)程很簡(jiǎn)單较性,注釋里已經(jīng)能描述清楚了。主要是調(diào)用onResume和設(shè)置狀態(tài)值结胀。
繼續(xù)看下android.app.Activity#performResume:

    final void performResume() {
        //里面會(huì)調(diào)用onRestart聲明和周期函數(shù)
        performRestart();
        ...
        mCalled = false;
        //依然是老套路赞咙,通過(guò)Instrumentation調(diào)用onResume,并且必須調(diào)用super方法
        mInstrumentation.callActivityOnResume(this);
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onResume()");
        }

        mCalled = false;
        
        //Fragement分發(fā)resume
        mFragments.dispatchResume();
        ...
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPostResume()");
        }
    }

onResume和onPause糟港、onCreate不同的是攀操,它會(huì)嘗試調(diào)用一下onRestart。
android.app.Activity#performRestart:

    final void performRestart() {
        mFragments.noteStateNotSaved();

        //很顯然這里是false秸抚,不會(huì)走進(jìn)去速和。只有當(dāng)退出Activity的時(shí)候才是true,才會(huì)調(diào)用onRestart聲明周期函數(shù)
        if (mStopped) {
            ....
            performStart();
        }
    }

這里也印證了啟動(dòng)Activity的聲明周期函數(shù)不包括onRestart剥汤。
android.app.Instrumentation#callActivityOnResume:

    public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();
        ...
    }

這里就是onResume的調(diào)用了气堕。

任務(wù)完成

我們知道周期函數(shù)是在looper里執(zhí)行的,跨進(jìn)程的調(diào)用實(shí)際上早已返回到了AMS中涩堤。當(dāng)Handler的LAUNCH_ACTIVITY消息發(fā)后缭受,這時(shí)AMS的主要任務(wù)就已經(jīng)完成了。而App進(jìn)程側(cè)完成了LAUNCH_ACTIVITY消息里的內(nèi)容后鹿驼,TargetActivity就算真正被啟動(dòng)了欲低。

總結(jié)

App進(jìn)程側(cè)的任務(wù)執(zhí)行都通過(guò)Looper來(lái)執(zhí)行的,不能一直阻塞著系統(tǒng)進(jìn)程畜晰。

  1. App進(jìn)程側(cè)記錄了構(gòu)造ActivityClientRecord砾莱,并且通過(guò)android.app.Activity#mToken映射好,存下來(lái)凄鼻,以備以后使用腊瑟。
  2. 調(diào)用了onCreate面哼、onStart
  3. 調(diào)用onResume。調(diào)用前檢查了是否需要調(diào)用onRestart扫步,發(fā)現(xiàn)不用調(diào)用魔策。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市河胎,隨后出現(xiàn)的幾起案子闯袒,更是在濱河造成了極大的恐慌,老刑警劉巖游岳,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件政敢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡胚迫,警方通過(guò)查閱死者的電腦和手機(jī)喷户,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)访锻,“玉大人褪尝,你說(shuō)我怎么就攤上這事∑谌” “怎么了河哑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)龟虎。 經(jīng)常有香客問(wèn)我璃谨,道長(zhǎng),這世上最難降的妖魔是什么鲤妥? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任佳吞,我火速辦了婚禮,結(jié)果婚禮上棉安,老公的妹妹穿的比我還像新娘底扳。我一直安慰自己,他們只是感情好垂券,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布花盐。 她就那樣靜靜地躺著,像睡著了一般菇爪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柒昏,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天凳宙,我揣著相機(jī)與錄音,去河邊找鬼职祷。 笑死氏涩,一個(gè)胖子當(dāng)著我的面吹牛届囚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播是尖,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼意系,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了饺汹?” 一聲冷哼從身側(cè)響起蛔添,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兜辞,沒(méi)想到半個(gè)月后迎瞧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逸吵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年凶硅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扫皱。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡足绅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出韩脑,到底是詐尸還是另有隱情编检,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布扰才,位于F島的核電站允懂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏衩匣。R本人自食惡果不足惜蕾总,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琅捏。 院中可真熱鬧生百,春花似錦、人聲如沸柄延。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)搜吧。三九已至市俊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間滤奈,已是汗流浹背摆昧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜒程,地道東北人绅你。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓伺帘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親忌锯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伪嫁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354