Activity的創(chuàng)建啟動(dòng)測(cè)量


僅用于自學(xué)總結(jié)骗爆,不便于他人學(xué)習(xí)败砂;如有閱讀赌渣,如有意見,望提探討昌犹;

Activity以及Application的創(chuàng)建 以及生命周期的調(diào)用坚芜;

程序入口ActivityThread.main 啟動(dòng),Activity的attach()方法中斜姥,IActivityManager通過AMS代理鸿竖,在AMS進(jìn)程中拿到pid和uid沧竟,組裝了Application的信息,通過Binder和ActivityThread通信缚忧,調(diào)用它的binfApplication方法悟泵,ActivityThread的該方法中通過Handler發(fā)消息,然后接收消息反射創(chuàng)建Application闪水,創(chuàng)建成功后調(diào)用了它的onCreate方法糕非;
AMS進(jìn)程中,bindApplication之后就通過attachApplicationLocked(app)等一系列調(diào)用球榆,調(diào)用到了ActivityThread#ApplicationThread#scheduleTransaction()方法朽肥,同樣經(jīng)過一系列調(diào)用也是通過Handler發(fā)消息,然后接收消息調(diào)用handleLaunchActivity持钉,該方法中調(diào)用performLaunchActivity反射創(chuàng)建Activity衡招,然后調(diào)用createActivityOnCreate->performCreate->onCreate();

setContentView

上面Activity的onCreate調(diào)用之后就是調(diào)用setContentView()方法

Activity.setContentView->getWindow().setContentView->PhoneWindow.setContentView

PhoneWindow是在Activity的attach中初始化的;

在PhoneWindow的setContentView方法中每强,對(duì)mContentParent進(jìn)行了創(chuàng)建賦值始腾,通過調(diào)用installDecor方法;最后通過布局加載器將我們傳進(jìn)來的main.xml解析到mContentParent中空执,mContentParent對(duì)應(yīng)著布局中的content布局浪箭;

在installDecor方法中,先初始化創(chuàng)建了DecorView辨绊,然后對(duì)一些window屬性進(jìn)行賦值處理山林;然后加載了mContentRoot布局,它內(nèi)部有兩個(gè)子View邢羔,一個(gè)是ActionBar驼抹,一個(gè)是content,其中content對(duì)應(yīng)著mContentParent拜鹤;將mContentRoot添加到DecorView中框冀,installSecor最后將mContentParent返回,然后同上所訴:最后通過布局加載器將我們傳進(jìn)來的main.xml解析到mContentParent中

到目前為止敏簿,通過setContentView方法明也,創(chuàng)建了DecorView和加載了我們提供的布局,但是這時(shí)惯裕,我們的View還是不可見的温数,因?yàn)槲覀儍H僅是加載了布局,并沒有對(duì)View進(jìn)行任何的測(cè)量蜻势、布局撑刺、繪制工作。

將DecorView添加至Window

每一個(gè)Activity組件都有一個(gè)關(guān)聯(lián)的Window對(duì)象握玛,用來描述一個(gè)應(yīng)用程序窗口够傍。每一個(gè)應(yīng)用程序窗口內(nèi)部又包含有一個(gè)View對(duì)象甫菠,用來描述應(yīng)用程序窗口的視圖。

還是說到上面的Activity的創(chuàng)建過程中冕屯,在最后啟動(dòng)Activity的部分寂诱,當(dāng)在Activity#onCreate中完成上面的setContentView()之后,ActivityThread#handleLaunchActivity還會(huì)調(diào)用ActivityThread#handResumeActivity方法安聘;
在該方法會(huì)獲取Window對(duì)象和DecorView對(duì)象痰洒;還會(huì)獲取WindowManager對(duì)象;
最后調(diào)用wm的addView(decor,l)方法浴韭;

WindowManager的實(shí)現(xiàn)類是WindowManagerImpl带迟,在他的addView(view)方法中,創(chuàng)建了ViewRootImpl對(duì)象囱桨,并調(diào)用了ViewRootImpl#setView(view),在該方法中嗅绰,通過WMS舍肠,從而將DecorView添加到了Window中;

ViewRootImpl窘面、DecorView翠语、WMS三者會(huì)彼此關(guān)聯(lián);

最后通過WMS調(diào)用ViewRootImpl#performTraerals方法來開始View的測(cè)量财边、布局肌括、繪制流程。

ViewRootImpl#performTraerals

該方法中會(huì)調(diào)用3個(gè)方法

  • performMeasure(childWidthMeasureSpe,childHeightMeasureSpec)
  • performLayout(lp,desiredWindowWidth,desiredWindowHeight)
  • performDraw()

Measure測(cè)量

MeasureSpec

MeasureSpec是一種測(cè)量存儲(chǔ)規(guī)格:他是一個(gè)32位的int值酣难,它的高兩位用來存儲(chǔ)mode谍夭,低30位用來存儲(chǔ)size;

performMeasure()

performMeasure(childWidthMeasureSpe,childHeightMeasureSpec)中的childWidthMeasureSpe,childHeightMeasureSpec都是通過getRootMeasureSpec方法獲取到的憨募;因?yàn)樗荄ecorView紧索,是第一個(gè)父View,所以它的測(cè)量規(guī)則是來自于屏幕寬高和自身的layoutParams菜谣;

該方法中通過判斷它的測(cè)量模式來確定珠漂,如果是確定尺寸則為確定尺寸,如果未MATCH_PARENT則為屏幕寬度尾膊,WRAP_CONTENT則為0到屏幕寬度之間媳危;

getRootMeasureSpec方法確定了mDecor的測(cè)量規(guī)則;

然后調(diào)用了performMeasure()方法冈敛;內(nèi)部mDecor.mesure(childWidthMeasureSpe,childHeightMeasureSpec)待笑,此時(shí)從頂級(jí)View開始了測(cè)量流程;

DecorView繼承于FrameLayout抓谴,DecorView.measure->FrameLayout.measure->View.measure->View.onMeasure->DecoreView.onMeasure->FrameLayout.onMeasure滋觉;

在該方法中签夭,遍歷子View,去掉GONE的View椎侠,然后對(duì)每一個(gè)子View進(jìn)行測(cè)量:measureChildWidthMargins第租;

測(cè)量之后保存測(cè)量結(jié)果:

setMeasureDimension(
        resolveSizeAndState(maxWidth,widthMeasureSpec,childState),
        resolveSizeAndState(maxHeight,heightMeasureSpec,childState)
)

View的測(cè)量

protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 1
}

內(nèi)部調(diào)用了getChildMeasureSpec方法;該方法中確立了子View如何獲取測(cè)量規(guī)則:

子View的LayoutParams\父容器SpecMode EXACTLY AT_MOST UNSPECIFIED
精確值(dp) EXACTLY childSize EXACTLY childSize EXACTLY childSize
match_parent EXACTLY parentSize AT_MOST parentSize UNSPECIFIED 0
wrap_content AT_MOST parentSize AT_MOST parentSize UNSPECIFIED 0

parentSize的意思是父View剩余的空間Size我纪;

  • 當(dāng)子View為EXACTLY時(shí)慎宾,無論父類是什么模式,都是取得childSize浅悉;
  • 當(dāng)子View為AT_MOST時(shí)趟据,父類無論是EXACTLY還是AT_MOST都是parentSize;因?yàn)槿绻竀iew是EXACTLY時(shí)术健,父View是將自己的剩余空間都給了子View汹碱;如果是AT_MOST,同理荞估,因?yàn)椴恢雷覸iew最大會(huì)用到多少咳促,所以把最大值給過去,子View根據(jù)自己的實(shí)際情況使用勘伺,但是不可以超過最大值跪腹;
  • 當(dāng)子View為MATCH時(shí),同AT_MOST理

上面的表格可以反饋出一個(gè)問題就是:

從頂層View開始測(cè)量飞醉,一種有4中形式(其實(shí)是3種)冲茸;

UNSPECIFIED暫不考慮;

如果父View是準(zhǔn)確值模式則直接往下傳測(cè)量準(zhǔn)則時(shí)缅帘,size就是準(zhǔn)確值轴术;
如果父View是match_parent時(shí),基本上就是當(dāng)前的所有size[pading+margin要去掉]
如果父View是wrap_content時(shí)钦无,則它的長(zhǎng)度可能是[0~maxSize]之間的值膳音,此時(shí)因?yàn)椴淮_定size,所以需要子類測(cè)量自己后來告訴父類size最大是多少铃诬,所以此時(shí)父類做的就是直接把maxSize下發(fā)給子類祭陷,然后子類再來根據(jù)自己的實(shí)際來測(cè)量;

這里其實(shí)一直有一個(gè)問題:
那就是當(dāng)父View為準(zhǔn)確值時(shí)趣席,這個(gè)沒問題兵志,但是當(dāng)它為AT_MOST時(shí),也就是父View的寬/高不確定[之后就只說寬宣肚,高同理]想罕,下發(fā)測(cè)量規(guī)則時(shí),總是把剩余寬度都下發(fā)過去,如果有的子View也是ViewGroup按价,且寬度也是AT_MOST惭适,那就繼續(xù)將該寬度[剩余寬度]下發(fā),一直到最后的子View中的onMeasure方法中

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

可以看到楼镐,在getDefaultSize中癞志,AT_MOST和EXACTLY是一樣的,取得是測(cè)量規(guī)則中的size框产,也就是說凄杯,如果該View的所有父View都沒有具體的size,那他的寬度就是DecorView的寬度秉宿,也就是屏幕寬度戒突,所以一般自定View,如果需要自定義的大小尺寸描睦,都需要重寫onMeasure方法膊存,區(qū)分一下match_parent和wrap_content的尺寸區(qū)別;

還有一個(gè)說法忱叭,很多人都說隔崎,針對(duì)這種父View是AT_MOST的情況,都是通過測(cè)量子View的尺寸從而再次測(cè)量自己的尺寸窑多;但是就我個(gè)人而言,我沒看到再次測(cè)量自己的方法調(diào)用洼滚,于是我很疑惑埂息,難道他不用再次測(cè)量自己?jiǎn)幔隙ㄐ枰囊0停蝗辉趺粗浪约旱臏y(cè)量大小;setMeasuredDimension方法只是保存了剩余的最大可能值千康,卻不是精確值,那他最后的精確值是怎么測(cè)量的呢铲掐?

我后來琢磨了一下拾弃,他不需要在測(cè)量這知道,它只需要將它所有的子View都測(cè)量一遍摆霉,然后在onLayout的時(shí)候豪椿,按照測(cè)量值進(jìn)行排布的時(shí)候就會(huì)知道最后的精確值,在onMeasure之后知道這個(gè)精確值也沒啥用携栋??jī)H僅是猜測(cè)搭盾;

又看了下,發(fā)現(xiàn)以上猜測(cè)不對(duì):有以上猜測(cè)純粹是因?yàn)榭吹紽rameLayout#onMeasure中首先編輯測(cè)量子View婉支,然后保存測(cè)量結(jié)果鸯隅;但是它后面又對(duì)FrameLayout為AT_MOST子View為MATCH_PARENT的情況進(jìn)行了出了,對(duì)該種情況下的子View進(jìn)行了二次測(cè)量向挖;然后就沒了蝌以;

我是看到了上方這樣才有此猜想炕舵;一時(shí)不解,然后去看到了LinearLayout#onMeasure跟畅,以為也會(huì)出現(xiàn)此種情形咽筋,結(jié)果發(fā)現(xiàn)LinearLayout將setMeasuredDimension放到了最后,也就是它確實(shí)是保存了準(zhǔn)確的測(cè)量結(jié)果碍彭;那FrameLayout#onMeasure又是怎么回事晤硕,細(xì)想一下就明白了,其實(shí)它在對(duì)子View進(jìn)行測(cè)量之后庇忌,取得最大值之后進(jìn)行保存舞箍,他的寬/高就已經(jīng)定了,不會(huì)發(fā)生改變皆疹,只剩剩下那種情況的二次測(cè)量疏橄,也只是在這種定了的情況下對(duì)那些子View為MATCH_PARENT的進(jìn)行具體值賦值,因?yàn)槭腔谀莻€(gè)最大值進(jìn)行操作略就,所以怎么也不會(huì)超過最大值捎迫,那就是最終保存的測(cè)量結(jié)果不會(huì)造成任何影響。

參考資料:
Android View源碼解讀:淺談DecorView與ViewRootImpl
Android View 測(cè)量流程(Measure)完全解析
Android View 布局流程(Layout)完全解析
Android View 繪制流程(Draw) 完全解析
Activity的創(chuàng)建表牢,和生命周期的調(diào)用
Activity XML 布局文件的加載
《Android開發(fā)藝術(shù)探索》第4章View的工作原理 -任玉剛


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窄绒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子崔兴,更是在濱河造成了極大的恐慌彰导,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敲茄,死亡現(xiàn)場(chǎng)離奇詭異位谋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)堰燎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門掏父,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秆剪,你說我怎么就攤上這事赊淑。” “怎么了仅讽?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵膏燃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我何什,道長(zhǎng)组哩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮伶贰,結(jié)果婚禮上蛛砰,老公的妹妹穿的比我還像新娘。我一直安慰自己黍衙,他們只是感情好泥畅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著琅翻,像睡著了一般位仁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上方椎,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天聂抢,我揣著相機(jī)與錄音,去河邊找鬼棠众。 笑死琳疏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的闸拿。 我是一名探鬼主播空盼,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼新荤!你這毒婦竟也來了揽趾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤苛骨,失蹤者是張志新(化名)和其女友劉穎篱瞎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體智袭,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奔缠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年掠抬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吼野。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡两波,死狀恐怖瞳步,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腰奋,我是刑警寧澤单起,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站劣坊,受9級(jí)特大地震影響嘀倒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一测蘑、第九天 我趴在偏房一處隱蔽的房頂上張望灌危。 院中可真熱鬧,春花似錦碳胳、人聲如沸勇蝙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽味混。三九已至,卻和暖如春诫惭,著一層夾襖步出監(jiān)牢的瞬間翁锡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工贝攒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盗誊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓隘弊,卻偏偏與公主長(zhǎng)得像哈踱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梨熙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349