Android View绘制流程
Posted 瞌睡先生想睡觉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android View绘制流程相关的知识,希望对你有一定的参考价值。
或许你已经看到过很多博客总结的View的绘制流程的.我这篇博客是跟着源码去看,对自己学到的知识加以印证.水平有限,仅供参考,如有错误,欢迎指正
我在之前的博客就已经说明了Activity的启动到显示的相关流程,现在我们来看下View具体的工作流程.
上次我们说到root.setView(view, wparams, panelParentView);这段代码,并没继续深入这段代码去看View的工作流程,现在接着这段代码往下阅读
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
synchronized (this)
if (mView == null)
mView = view;//保存根布局
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);//保存LayoutParams参数
if (mWindowAttributes.packageName == null)
mWindowAttributes.packageName = mBasePackageName;
attrs = mWindowAttributes;
......
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();//在这里会开始调用View的三大流程
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)
mInputChannel = new InputChannel();
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);//通过WindowSession来完成Window添加的过程,这里不作赘述
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();
......
@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.");//只有创建ViewRootImpl的线程才能触及它的视图
void scheduleTraversals()
if (!mTraversalScheduled)
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//内部Handler实现,执行mTraversalRunnable
if (!mUnbufferedInputDispatch)
scheduleConsumeBatchedInput();
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//实现Runnable接口
final class TraversalRunnable implements Runnable
@Override
public void run()
doTraversal();
void doTraversal()
if (mTraversalScheduled)
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile)
Debug.startMethodTracing("ViewAncestor");
performTraversals();//这个就是在其他博客上经常看到的方法,在这个方法里面里面会开始View的三大流程
if (mProfile)
Debug.stopMethodTracing();
mProfile = false;
//这个方法里面有着大量的代码,我们省略和我们要看的无关主流程的代码
private void performTraversals()
// cache mView since it is used so much below...
final View host = mView;
if (DBG)
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
if (host == null || !mAdded)
return;
......
final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout)
mForceNextWindowRelayout = false;
......
try
......
final boolean freeformResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
final boolean dockedResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
final boolean dragResizing = freeformResizing || dockedResizing;
if (mDragResizing != dragResizing)
if (dragResizing)
mResizeMode = freeformResizing
? RESIZE_MODE_FREEFORM
: RESIZE_MODE_DOCKED_DIVIDER;
startDragResizing(mPendingBackDropFrame,
mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
mPendingStableInsets, mResizeMode);
else
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
if (!USE_MT_RENDERER)
if (dragResizing)
mCanvasOffsetX = mWinFrame.left;
mCanvasOffsetY = mWinFrame.top;
else
mCanvasOffsetX = mCanvasOffsetY = 0;
catch (RemoteException e)
if (!mStopped || mReportNextDraw)
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration)
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);//获取根布局的MeasureSpec,mWidth就是window的宽度,lp是之前在setView方法中保存的数据,是根视图的宽度
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行performMeasure方法,开始Measure流程
// 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(mTag,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
layoutRequested = true;
else
// Not the first pass and no window/insets/visibility change but the window
// may have moved and we need check that and if so to update the left and right
// in the attach info. We translate only the window frame since on window move
// the window manager tells us only for the new frame but the insets are the
// same and we do not want to translate them more than once.
maybeHandleWindowMove(frame);
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout)
performLayout(lp, mWidth, mHeight);//Layout流程
// 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)
if (DBG)
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
host.debug();
......
if (!cancelDraw && !newSurface)
if (mPendingTransitions != null && mPendingTransitions.size() > 0)
for (int i = 0; i < mPendingTransitions.size(); ++i)
mPendingTransitions.get(i).startChangingAnimations();
mPendingTransitions.clear();
performDraw();//Draw流程
else
if (isViewVisible)
// 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;
//获取根布局的MeasureSpec
private static int getRootMeasureSpec(int windowSize, int rootDimension)
int measureSpec;
switch (rootDimension)
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);//根视图的大小就是窗口的大小
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);//根视图的大小是根视图的最大值,且不能超过window的大小
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);//根视图的大小就是根视图本身的大小
break;
return measureSpec;
measure
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)
if (mView == null)
return;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);//调用根视图的measure方法
finally
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
//View的measure方法,这个方法被final修饰不可进行重写,所以所有的View都是用的同一个measure方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec)
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent))
Insets insets = getOpticalInsets();
int oWidth = insets.left + insets.right;
int oHeight = insets.top + insets.bottom;
widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
// Suppress sign extension for the low bytes
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
// Optimize layout by avoiding an extra EXACTLY pass when the view is
// already measured as the correct size. In API 23 and below, thi以上是关于Android View绘制流程的主要内容,如果未能解决你的问题,请参考以下文章