首先要明白invalidate()方法是做什么的?
View#invalidate():
/**
* Invalidate the whole view. If the view is visible,
* {@link #onDraw(android.graphics.Canvas)} will be called at some point in
* the future.
* <p>
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*/
public void invalidate() {
invalidate(true);
}
英文注释的大致意思是:如果View是可见的,使整个View视图无效,然后在未来的某个时间点View的onDraw(android.graphics.Canvas)方法将被调用。
注意: 该方法必须在UI线程,也就是主线程中才能调用。如果要在非UI线程中调用,可以调用View#postInvalidate() 方法,这也是invalidate()和postInvalidate()的区别之一;
也就是说,invalidate()方法是用来刷新重绘当前的View的,如果当前View的布局尺寸、位置没有变化,仅仅是绘制内容变化了,那么我们就可以调用invalidate()方法。
跟踪源码进入 View#invalidate(boolean invalidateCache):
@UnsupportedAppUsage
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
// View#invalidate(boolean invalidateCache)方法内部,又调用了下面的方法
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
// 这里挑重点代码查看,以免迷失在代码的海洋中,与源码流程分析无关的代码,暂时不关注
......省略代码
// 这里判断该子View是否可见或者是否处于动画中,如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘
if (skipInvalidate()) {
return;
}
......省略代码
// 根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
// 设置PFLAG_DIRTY标记位
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
// 子View的ViewParent就是它的父View即ViewGroup
// 调用了父类ViewParent的invalidateChild()方法
p.invalidateChild(this, damage);
}
// Damage the entire projection receiver, if necessary.
......省略代码
}
}
该方法中,首先判断该子View是否可见或者是否处于动画中,如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘。然后根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘。最后调用了父类ViewParent的invalidateChild()方法,子View的ViewParent就是它的父View即ViewGroup。
跟进ViewGroup#invalidateChild(View child, final Rect dirty) 方法:
public final void invalidateChild(View child, final Rect dirty) {
......省略代码
// parent为当前的ViewGroup
ViewParent parent = this;
if (attachInfo != null) {
final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
Matrix childMatrix = child.getMatrix();
final boolean isOpaque = child.isOpaque() && !drawAnimation &&
child.getAnimation() == null && childMatrix.isIdentity();
int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
......省略代码
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
} else if (parent instanceof ViewRootImpl) {
((ViewRootImpl) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null) {
if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
view.getSolidColor() == 0) {
opaqueFlag = PFLAG_DIRTY;
}
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
// 标志位的设置
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
}
}
// 调用parent的invalidateChildInParent,即调用ViewGroup的invalidateChildInParent
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
}
}
ViewGroup#invalidateChild(View child, final Rect dirty) 方法内部,不断的do while循环,直到循环到最外层view的invalidateChildInParent方法。
内层的parent是调用的ViewGroup的invalidateChildInParent方法。
最外层的View,即DecorView,也就是调用DecorView的ViewParent#invalidateChildInParent方法;
那么,DecorView的ViewParent是什么呢?上面的图,给出了答案,是ViewRootImpl。下面我们从源码中探寻究竟为何是ViewRootImpl:
在Activity中,当调用 setContentView() 方法后,经过installDecor() -> generateLayout(mDecor) -> mLayoutInflater.inflate(layoutResID, mContentParent)等方法调用后,Activity的布局文件就已成功添加到了DecorView的mContentParent中,此时,DecorView还未添加到Window中。
Activity调用onResume方法,然后调用makeVisible方法后,DecorView才被添加到Window中。
这里简要分析一下Activity的启动过程,不做过多分析,简单写一下,顺序调用ActivityThread的handleLauncheActivity,handleResumeActivity方法,handleResumeActivity方法中首先调用performResumeActivity方法,performResumeActivity方法中调用Activity中的performResume方法,之后调用Activity的onResume方法,最后调用Activity的makeVisible方法,makeVisible方法中会把当前的顶层DecoView通过WindowManager的addView方法添加到WindowManager中,而WindowManager的实现类WindowManagerImpl中调用的是WindowManagerGlobal的addView方法。
如下: 看注释的关系代码,这里实例化了一个ViewRootImpl,调用了setView方法,传入的view参数就是DecorView;
WindowManagerGlobal#addView:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
......省略代码
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
......省略代码
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 {
// view即为addView传入的DecorView
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
ViewRootImpl#setView(View view, WindowManager.LayoutParams attrs, View panelParentView):
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
......省略代码
// 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();
// 设置DecorView的viewParent为ViewRootImpl
view.assignParent(this);
mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
if (mAccessibilityManager.isEnabled()) {
mAccessibilityInteractionConnectionManager.ensureConnection();
}
if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
// Set up the input pipeline.
......省略代码
}
}
ViewRootImpl#setView()方法,其中的参数view即为DecorView,在该方法里面调用了view.assigenParent(this),把ViewRootImpl设置为DecorView的ViewParent。
插入了一段小插曲,下面回头跟踪查看
ViewGroup#invalidateChildInParent(final int[] location, final Rect dirty):
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
// either DRAWN, or DRAWING_CACHE_VALID
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
!= FLAG_OPTIMIZE_INVALIDATE) {
// 调用offset方法,把当前dirty区域的坐标转化为父容器中的坐标
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
location[CHILD_TOP_INDEX] - mScrollY);
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
// 调用union方法,把子dirty区域与父容器的区域求并集,换句话说,dirty区域变成父容器区域
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
final int left = mLeft;
final int top = mTop;
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
dirty.setEmpty();
}
}
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
} else {
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
} else {
// in case the dirty rect extends outside the bounds of this container
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
location[CHILD_LEFT_INDEX] = mLeft;
location[CHILD_TOP_INDEX] = mTop;
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
// 返回当前View的父容器,以便进行下一次循环
return mParent;
}
return null;
}
调用offset方法,把当前dirty区域的坐标转化为父容器中的坐标。接着调用union方法,把子dirty区域与父容器的区域求并集,换句话说,dirty区域变成父容器区域。最后返回当前视图的父容器,以便进行下一次循环。
由于ViewGroup#invalidateChild() 方法里面的do while循环完最终会调用最外层 ViewRootImpl 里面的 invaludateChild 方法。
ViewRootImpl#invalidateChild(View child, Rect dirty):
@Override
public void invalidateChild(View child, Rect dirty) {
// 内部调用了invalidateChildInParent方法
invalidateChildInParent(null, dirty);
}
ViewRootImpl#invalidateChildInParent(int[] location, Rect dirty):
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
// 最后调用 invalidateRectOnScreen 方法
invalidateRectOnScreen(dirty);
return null;
}
该方法主要还是dirty区域的计算。然后调用
ViewRootImpl#invalidateRectOnScreen(Rect dirty):
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
// Add the new dirty rect to the current one
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
if (!intersected) {
localDirty.setEmpty();
}
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
// 重点走到这里
scheduleTraversals();
}
}
该方法主要是刷新屏幕上面的一个Rect区域,而Rect区域就是调用invalidate方法的那个View大小。然后调用了ViewRootImpl的scheduleTraversals() 方法。
ViewRootImpl#scheduleTraversals():
@UnsupportedAppUsage
void scheduleTraversals() {
// 注意这个标志位,多次调用 requestLayout,只有当这个标志位为 false 时才有效
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 通过postSyncBarrier()设置Handler消息的同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// Choreographer 通过 postCallback 提交一个任务,mTraversalRunnable是要执行的回调
// 有了同步屏障mTraversalRunnable就会被优先执行
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Choreographer 通过 postCallback 提交一个任务,mTraversalRunnable是要执行的回调,有了同步屏障mTraversalRunnable就会被优先执行,至于为何有了同步屏障mTraversalRunnable就会被优先执行?可以查看分析Handler之同步屏障机制与Android的屏幕刷新机制在源码中的应用
ViewRootImpl#TraversalRunnable:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
TraversalRunnable 任务方法中又调用了doTraversal() 方法。
ViewRootImpl#doTraversal() :
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
// 走到这里,这里也是很多博客开始分析View的绘制流程时,选择的切入入口
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals() 这个方法非常重要,方法非常多,简单讲我们需要关注以下几个方法:
ViewRootImpl#performTraversals() :
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
mIsInTraversal = true;
......省略代码
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
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);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
// 关注方法 1 performMeasure 测量方法
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 (measureAgain) {
// 关注方法 1 performMeasure 测量方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
maybeHandleWindowMove(frame);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
// 关注方法 2 performLayout 位置摆放方法
performLayout(lp, mWidth, mHeight);
......省略代码
}
......省略代码
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
// 关注方法 3 performDraw 绘制方法
performDraw();
} else {
......省略代码
}
mIsInTraversal = false;
}
ViewRootImpl#performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) :
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
// 调用View的measure方法,measure方法内部又会调用View的onMeasure方法对View进行测量
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
该方法内部调用了View的measure方法,measure方法内部又会调用View的onMeasure方法对View进行测量。
ViewRootImpl#performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight):
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
if (host == null) {
return;
}
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
......省略代码
if (validLayoutRequesters != null) {
......省略代码
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
......省略代码
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
该方法内部调用了View的layout方法,layout方法内部又会调用View的onLayout方法对View进行布局摆放。
ViewRootImpl#performDraw() :
private void performDraw() {
......省略代码
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
该方法中调用了ViewRootImpl#draw()方法,ViewRootImpl#draw()方法又调用了ViewRootImpl#drawSoftware()方法,ViewRootImpl#drawSoftware()方法中,当前的View调用了View#draw() 方法,即mView.draw(canvas)对当前的View进行绘制;
ViewRootImpl#performTraversals() 方法依次可能会调用了performMeasure,performLayout,performDraw。这三个和我们常用的onMeasure,onLayout,onDraw方法就很像,因为这三个方法最终也会调用我们常用的onXXX方法。但在这里这三个方法不一定都会调用,当我们调用invalidate的时候,也就是说我们只想调用绘制我们的View的方法,这个时候只会调用到performDraw方法;当我们的view如果位置发生改变了,则也会调用到performLayout方法;如果大小也改变了,则也会调用perforMeasure方法。这三个方法就会回调View里面的mesure,layout,draw方法,measure内部会回调onMeasure,layout内部会回调onLayout,draw内部会回调onDraw。
至此,View#invalidate() 方法的源码流程分析完毕;