在Activity布局加載流程源碼解析一文中,我們分析了Activity布局加載流程,通過(guò)分析我們了解到Activity通過(guò)Window來(lái)控制界面的展示,一個(gè)Activity包含一個(gè)Window對(duì)象(具體由PhoneWindow來(lái)實(shí)現(xiàn)),并且PhoneWindow將DecorView作為整個(gè)應(yīng)用窗口的根View锡搜。當(dāng)時(shí),我們?cè)诜治龅臅r(shí)候遺留了一個(gè)問(wèn)題瞧掺,那就是DecorView是什么時(shí)候添加到Window上去的?那么凡傅,這篇文章就來(lái)具體介紹一下Activity布局繪制流程的源碼分析辟狈,而且源碼版本基于Android 8.0。
一夏跷、從ActivityThread的handleResumeActivity方法說(shuō)起
在Activity的onCreate()方法中哼转,我們通過(guò)setContentView()方法完成了xml布局解析以及DecorView的創(chuàng)建。之后槽华,DecorView會(huì)被添加到Window上去壹蔓,這個(gè)過(guò)程的起始點(diǎn)在ActivityThread的handleResumeActivity方法中。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
... ...
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
... ...
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
... ...
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManager.getService()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
該方法的實(shí)現(xiàn)代碼比較多猫态,我們只關(guān)注重點(diǎn)部分佣蓉。由源碼可知,在獲取了Activity的Window相關(guān)參數(shù)之后亲雪,執(zhí)行了r.activity.makeVisible()方法勇凭,即接下來(lái)回去執(zhí)行Activity的makeVisible()方法,這個(gè)方法就是DecorView被添加到Window上去的起點(diǎn)义辕。
二虾标、Activity中的makeVisible方法
makeVisible字面上的意思是讓它變?yōu)榭梢?jiàn),我們看一下它的具體實(shí)現(xiàn):
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
由源碼可知灌砖,這里的mWindowAdded是一個(gè)布爾類(lèi)型的成員變量璧函,很明顯這里的主要邏輯if分支只會(huì)執(zhí)行一次,因?yàn)閳?zhí)行過(guò)后mWindowAdded就會(huì)被賦值為true基显,之后便再也不會(huì)執(zhí)行if分支代碼蘸吓。緊接著,通過(guò)getWindowManager()方法獲取ViewManager對(duì)象撩幽,我們看一下具體實(shí)現(xiàn)美澳。
// Activity # getWindowManager()
/** Retrieve the window manager for showing custom windows. */
public WindowManager getWindowManager() {
return mWindowManager;
}
從方法的注釋來(lái)看是返回一個(gè)Window管理器,并且是以成員變量的形式返回。那么制跟,很自然我們就要去尋找它是在哪邊被賦值的舅桩,通過(guò)代碼檢索,我們很容易發(fā)現(xiàn)它是在Activity的attch()方法中被賦值的雨膨。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
... ...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
}
這邊我稍微精簡(jiǎn)了一下代碼擂涛,我們可以看到mWindowManager是通過(guò)PhoneWindow的getWindowManager()方法返回的。但是聊记,通過(guò)查看源碼撒妈,我們可以發(fā)現(xiàn)PhoneWindow繼承于Window,而且沒(méi)有重寫(xiě)getWindowManager()方法排监,所以最終還是會(huì)調(diào)用到Window的getWindowManager()方法狰右。
// Window # getWindowManager()
/**
* Return the window manager allowing this Window to display its own
* windows.
*
* @return WindowManager The ViewManager.
*/
public WindowManager getWindowManager() {
return mWindowManager;
}
好吧,Window的getWindowManager()方法同樣是返回它的成員變量舆床,所以我們依舊需要去找它是在哪邊被賦值的棋蚌。這個(gè)還是比較好找的,最終我們可以發(fā)現(xiàn)它是在setWindowManager()方法中被賦值的挨队。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
由源碼可知谷暮,mWindowManager最終是通過(guò)WindowManagerImpl的createLocalWindowManager()方法創(chuàng)建的。
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
可以看到盛垦,這個(gè)方法很簡(jiǎn)單湿弦,就是通過(guò)new的方式創(chuàng)建了一個(gè)WindowManagerImpl對(duì)象并返回。兜了這么一大圈腾夯,我們終于知道Activity的makeVisible()方法中g(shù)etWindowManager()返回的其實(shí)就是一個(gè)WindowManagerImpl對(duì)象颊埃。剛好,我們順便理一下繼承關(guān)系蝶俱,就是WindowManage繼承自ViewManager竟秫,而WindowManagerImpl又繼承自WindowManage。
接下來(lái)跷乐,我們繼續(xù)分析Activity的makeVisible()方法肥败,在通過(guò)getWindowManager()方法獲取到WindowManagerImpl對(duì)象之后,接著執(zhí)行了如下方法:
wm.addView(mDecor, getWindow().getAttributes());
通過(guò)以上分析愕提,我們很容易得知馒稍,這其實(shí)就是去調(diào)用WindowManagerImpl的addView方法,并且把DecorView作為入?yún)ⅰ?/p>
// WindowManagerImpl # addView()
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
由源碼可知浅侨,mGlobal是一個(gè)WindowManagerGlobal的單例對(duì)象纽谒,是Window處理的工具類(lèi),用于操作View組件如输。我們具體看一下mGlobal的addView()方法的實(shí)現(xiàn):
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
... ...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
... ...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
... ...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
由于代碼實(shí)現(xiàn)較長(zhǎng)鼓黔,稍微做了一下精簡(jiǎn)央勒,我們只關(guān)注重點(diǎn)部分。首先說(shuō)一下上述代碼中的3個(gè)ArrayList澳化,其中mViews主要用于保存Activity的DecorView(Activity的根View)崔步,mRoots主要用于保存ViewRootImpl,mParams主要用于保存Window的LayoutParams缎谷。最后井濒,調(diào)用了ViewRootImpl的setView()方法,這個(gè)方法就很關(guān)鍵了列林,將在下面進(jìn)行具體分析瑞你。
三、ViewRootImpl的setView()方法
在ViewRootImpl的setView()方法希痴,會(huì)完成對(duì)View的測(cè)量者甲、布局和繪制。并且砌创,通過(guò)Binder跨進(jìn)程的方式向WMS(WindowManagerService)發(fā)起一個(gè)調(diào)用虏缸,從而將DecorView最終添加到Window上。在這個(gè)過(guò)程中纺铭,ViewRootImpl、DecorView和WMS會(huì)彼此向相互關(guān)聯(lián)刀疙。
我們先來(lái)看一下View的測(cè)量舶赔、布局和繪制過(guò)程,在ViewRootImpl的setView()方法中谦秧,會(huì)調(diào)用requestLayout()方法竟纳。
// ViewRootImpl # setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
}
下面我們看下requestLayout()方法的具體實(shí)現(xiàn):
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
我們可以看到,這里首先回去調(diào)用checkThread()方法疚鲤,主要檢查當(dāng)前線程是否為UI線程锥累,若當(dāng)前線程非UI線程,則拋出非UI線程更新UI的錯(cuò)誤集歇。其中桶略,mThread成員變量是在ViewRootImpl的構(gòu)造方法中被賦值的,而ViewRootImpl的構(gòu)造方法又是在UI線程中被調(diào)用的诲宇,所以mThread自然也就被賦值為UI線程际歼。在完成UI線程檢查之后,接著會(huì)去調(diào)用scheduleTraversals()方法姑蓝,我們看下它的具體實(shí)現(xiàn):
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
由源碼可知鹅心,mChoreographer.postCallback,內(nèi)部會(huì)調(diào)用一個(gè)異步消息纺荧,用于執(zhí)行mTraversalRunnable的run()方法旭愧,我們來(lái)具體看一下mTraversalRunnable的具體定義:
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
很明顯颅筋,最后后調(diào)用到run()方法里面的doTraversal()方法,我們一起看下输枯。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
我們可以看到议泵,doTraversal()方法內(nèi)部又會(huì)去調(diào)用performTraversals()方法,其實(shí)這個(gè)方法就是整個(gè)View的繪制流程的起始方法用押,我們一起來(lái)看下肢簿,這個(gè)方法非常非常的長(zhǎng),我們只關(guān)注重點(diǎn)部分的代碼蜻拨。
private void performTraversals() {
... ...
// View測(cè)量
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(TAG,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
}
... ...
// View布局
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
}
... ...
// View繪制
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
由源碼可知池充,在performTraversals()方法中,主要是依次調(diào)用了performMeasure()缎讼,performLayout()收夸,performDraw()這三個(gè)方法,用于完成View的測(cè)量血崭、布局和繪制卧惜。
View的測(cè)量
我們一起先來(lái)看下performMeasure()方法的具體實(shí)現(xiàn)。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
我們看到夹纫,在該方法中調(diào)用了mView的measure()方法咽瓷,通過(guò)源碼追蹤我們可以很容易發(fā)現(xiàn)這個(gè)成員變量mView其實(shí)就是傳遞進(jìn)來(lái)的DecorView對(duì)象,所以最終調(diào)用的是DecorView的measure()方法舰讹,我們具體看下茅姜。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
... ...
if (forceLayout || needsLayout) {
... ...
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
... ...
}
我們可以看到,measure方法主要是做一些判斷月匣,如果View需要測(cè)量就會(huì)去調(diào)用它的onMeasure()方法钻洒。下面,我們就來(lái)看一下DecorView的onMeasure()方法锄开。
// DecorView # onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = getMode(widthMeasureSpec);
final int heightMode = getMode(heightMeasureSpec);
... ...
boolean measure = false;
... ...
if (!fixedWidth && widthMode == AT_MOST) {
... ...
if (width < min) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
measure = true;
}
}
}
if (measure) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
我們可以看到素标,DecorView的onMeasure()方法主要是用來(lái)判斷DecorView需不需要測(cè)量,如果需要測(cè)量萍悴,則調(diào)用其父類(lèi)FrameLayout的onMeasure()方法头遭。
// FrameLayout # onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
... ...
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
... ...
final int childHeightMeasureSpec;
... ...
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
由源碼可知,這里主要通過(guò)for循環(huán)癣诱,獲取該View的所有子View任岸,并執(zhí)行所有子View的measure方法,如果子View是ViewGroup就會(huì)調(diào)用ViewGroup的onMeasure()方法狡刘,若果子View是View就會(huì)調(diào)用View的onMeasure()方法享潜。因此,我們還需要看一下View的onMeasure()方法嗅蔬。
// View # onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
這樣剑按,通過(guò)一系列的方法調(diào)用就可以把View即其子View的大小測(cè)量出來(lái)了疾就,并且保存在了成員變量mMeasuredWith和mMeasuredHeight中。
View的布局
回到performTraversals()方法中艺蝴,我們繼續(xù)看performLayout()方法猬腰。
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
... ...
final View host = mView;
if (host == null) {
return;
}
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
... ...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
我們可以看到,該方法中主要調(diào)用了host的layout()方法猜敢,而host又是通過(guò)mView(DecorView)賦值的姑荷,所以就是去調(diào)用DecorView的layout()方法。通過(guò)源碼可以發(fā)現(xiàn)最終調(diào)用的是ViewGroup的layout()方法缩擂。
// ViewGroup # layout()
@Override
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
mLayoutCalledWhileSuppressed = true;
}
}
好吧鼠冕,這里又調(diào)用了其父類(lèi)的layout()方法,所以又會(huì)去調(diào)用View的layout()方法胯盯。
public void layout(int l, int t, int r, int b) {
... ...
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
... ...
}
}
我們可以看到懈费,layout()方法主要還是做一些判斷,如果View有變化需要重新布局博脑,則會(huì)去調(diào)用它的onLayout()方法憎乙。所以,接下來(lái)我們需要看一下DecorView的onLayout()方法叉趣。
// DecorView # onLayout()
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
getOutsets(mOutsets);
if (mOutsets.left > 0) {
offsetLeftAndRight(-mOutsets.left);
}
if (mOutsets.top > 0) {
offsetTopAndBottom(-mOutsets.top);
}
if (mApplyFloatingVerticalInsets) {
offsetTopAndBottom(mFloatingInsets.top);
}
if (mApplyFloatingHorizontalInsets) {
offsetLeftAndRight(mFloatingInsets.left);
}
// If the application changed its SystemUI metrics, we might also have to adapt
// our shadow elevation.
updateElevation();
mAllowUpdateElevation = true;
if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
getViewRootImpl().requestInvalidateRootRenderNode();
}
}
我們看到這個(gè)方法中又會(huì)去調(diào)用其父類(lèi)Framelayout的onLayout()方法泞边。
// Framelayout # onLayout()
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
... ...
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
Framelayout的onLayout()方法又會(huì)去調(diào)用layoutChildren()方法,具體代碼上面已經(jīng)貼出來(lái)了疗杉。與measure類(lèi)似阵谚,這里也是通過(guò)for循環(huán)獲取該View的所有子View,如果子View是ViewGroup則執(zhí)行ViewGroup的layout()方法乡数,如果子View是View則執(zhí)行View的layout()方法椭蹄。
這樣闻牡,通過(guò)一系列的方法調(diào)用就可以把View及其子View的位置信息計(jì)算出來(lái)并保存在成員變量中净赴。
View的繪制
好了,我們?cè)俅位氐絧erformTraversals()方法中罩润,繼續(xù)看performDraw()方法玖翅。
private void performDraw() {
... ...
draw(fullRedrawNeeded);
... ...
}
private void draw(boolean fullRedrawNeeded) {
... ...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
... ...
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
... ...
mView.draw(canvas);
... ...
return true;
}
對(duì)源碼做了大量的精簡(jiǎn),我們看到最后會(huì)輾轉(zhuǎn)調(diào)用到mView的draw()方法割以,也就是調(diào)用DecorView的draw()方法金度。
// DecorView # draw()
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
DecorView的draw()方法又會(huì)去調(diào)用其父類(lèi)FrameLayout的draw()方法,最終又會(huì)調(diào)用到View的draw()方法严沥,一起來(lái)看下猜极。
// View # draw()
@CallSuper
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
/*
* Here we do the full fledged routine...
* (this is an uncommon case where speed matters less,
* this is why we repeat some of the tests that have been
* done above)
*/
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
// Step 2, save the canvas' layers
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
// overlapping fades produce odd-looking artifacts
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
// also clip horizontal fades if necessary
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
}
我們可以看到,View的整個(gè)繪制流程還是比較清楚的消玄,整個(gè)執(zhí)行邏輯一共大概需要六步跟伏,并且在執(zhí)行draw()方法的過(guò)程中丢胚,如果包含子View,那么也會(huì)執(zhí)行子View的draw()方法受扳。這樣携龟,經(jīng)過(guò)一系列的方法調(diào)用之后,DectorView及其子View就被繪制出來(lái)了勘高。
至此峡蟋,ViewRootImpl的setView()方法中的requestLayout()部分的分析差不多已經(jīng)結(jié)束了。
四华望、DecorView添加到Window上
我們回到ViewRootImpl的setView()方法中蕊蝗,requestLayout()方法調(diào)用完畢之后,View的測(cè)量立美、布局和繪制已經(jīng)完成了匿又,下面就是要把DecorView真正添加到Window上去了。
// ViewRootImpl # setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
...
}
我們可以看到建蹄,通過(guò)mWindowSession的addToDisplay()方法調(diào)用碌更,將DecorView添加到Window上去。我們先看一下mWindowSession是如何賦值的洞慎。
mWindowSession = WindowManagerGlobal.getWindowSession();
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
熟悉Binder跨進(jìn)程通信的同學(xué)應(yīng)該知道痛单,最后應(yīng)該是通過(guò)WindowManagerService的openSession()方法獲取到了mWindowSession對(duì)象。
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
我們看到openSession()方法通過(guò)new的方式返回了一個(gè)IWindowSession類(lèi)型的對(duì)象劲腿。
回到ViewRootImpl的setView()方法旭绒,我們看到它調(diào)用了mWindowSession的addToDisplay()方法,這里也就是調(diào)用了Session的addToDisplay()方法焦人。
// Session # addToDisplay()
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
由源碼可知挥吵,這里的成員變量mService是WindowManagerService對(duì)象,所以最終會(huì)去調(diào)用WindowManagerService的addWindow()方法花椭。因此忽匈,最終是由WindowManagerService來(lái)完成將DecorView添加到Window上。