僅用于自學(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的工作原理 -任玉剛