前言
在分析invalidate是如何请求View重绘的之前,我们先看下invalidate与postInvalidate区别
invalidate与postInvalidate区别
nvalidate与postInvadlidate都是用于请求View重绘的API,invalidate在主线程中进行调用,而postInvadlidate则在子线程中进行调用。 我们来分析下postInvadlidate的源码 :
public void postInvalidate() {
postInvalidateDelayed(0);
}
postInvalidate()蒋会调用postInvalidateDelayed(0)方法,继续跟进。
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
postInvalidateDelayed方法,通过attachInfo获取到当前的ViewRootImpl对象,调用它的dispatchInvalidateDelayed方法
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
从上面的源码已经可以看出,postInvalidate的子线程这一个特性了。再继续跟下去看看。
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
.....
}
}
代码跟到这里,也就明白了,postInvalidate通过sendMessageDelayed的方法,加入到了looper中,之后在handleMessage中再调用对应View的invalidate()方法,请求View重绘。
invalidate流程分析
现在我们来看看invalidate是如何让View进行重绘的呢?
流程图
我们的旅程从View的invalidate传递过程开始
现在来看看View#invalidate()方法。
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
invalidate调用View#invalidateInternal方法传入当前View的位置参数
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
....
//View是否可见,是否在动画运行中
if (skipInvalidate()) {
return;
}
// View的标记防止多次invalidate
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;
}
// 设置标志,表明View正在被重绘
mPrivateFlags |= PFLAG_DIRTY;
//清除缓存,设置标志,表明重绘由当前View发起
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// 把需要重绘的View区域传递给父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的方法,向上传递重绘区域,并更新他的绘制区域,所有这个方法后面核心目的就是计算canvas 需要重新绘制区域计算的方法
p.invalidateChild(this, damage);
}
...
}
}
上述代码中,会判断当前View的状态,是否需要进行重绘,之后设置一系列标记位。通过父View的invalidateChild(this, damage)方法,将需要重绘的区域传递给父View。 接着来看下ViewGroup#invalidateChild方法,这里仅截取了其中的主要代码
public final void invalidateChild(View child, final Rect dirty) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null && attachInfo.mHardwareAccelerated) {
// 硬件加速,这里不需要关心,这次只是
onDescendantInvalidated(child, child);
return;
}
ViewParent parent = this;
if (attachInfo != null) {
// If the child is drawing an animation, we want to copy this flag onto
// ourselves and the parent to make sure the invalidate request goes
// through
final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
// Check whether the child that requests the invalidate is fully opaque
// Views being animated or transformed are not considered opaque because we may
// be invalidating their old position and need the parent to paint behind them.
Matrix childMatrix = child.getMatrix();
final boolean isOpaque = child.isOpaque() && !drawAnimation &&
child.getAnimation() == null && childMatrix.isIdentity();
// Mark the child as dirty, using the appropriate flag
// Make sure we do not set both flags at the same time
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;
//判断child的Matrix是否不一致 或者 特殊View的判断条件这里不需要关注
if (!childMatrix.isIdentity() ||
(mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
Matrix transformMatrix;
if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
Transformation t = attachInfo.mTmpTransformation;
boolean transformed = getChildStaticTransformation(child, t);
if (transformed) {
transformMatrix = attachInfo.mTmpMatrix;
transformMatrix.set(t.getMatrix());
if (!childMatrix.isIdentity()) {
transformMatrix.preConcat(childMatrix);
}
} else {
transformMatrix = childMatrix;
}
} else {
transformMatrix = childMatrix;
}
transformMatrix.mapRect(boundingRect);
// 设置重绘的区域
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
// 下面是循环遍历设置child绘制区域,直到遍历到ViewRoot
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 = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
// 如果 parent 的也需要重绘,重新计算绘制区域
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);
}
}
上述代码中,设置了需要重绘的区域dirty。之后再do...while方法中,反复的调用**parent = parent.invalidateChildInParent(location, dirty)**方法,来调用父类的invalidateChildInParent对View的重绘请求进行传递。这里的parent有可能是ViewGroup,也有可能是ViewRoot,我们先来看看ViewGroup#invalidateChildInParent方法
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
(mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
FLAG_OPTIMIZE_INVALIDATE) {
// 子View中的布局位置转换为父View中的布局位置
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
location[CHILD_TOP_INDEX] - mScrollY);
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
// 合并绘制区域集合
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();
}
}
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
return mParent;
} else {
mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX] = mLeft;
location[CHILD_TOP_INDEX] = mTop;
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);
}
if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
return mParent;
}
}
return null;
}
在上述代码中,将会使用offset,把子View需要重绘的坐标区域转换为父View中的坐标区域。之后使用union对子View与父View的区域进行集合运算,获得需要绘制的区域。 接下来我们再来看看ViewRoot#invalidateChildInParent方法,ViewRoot并不是View,ViewRoot的实现类为ViewRootImpl,我们来看下它的invalidateChildInParent方法。
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
// 检查线程是否为创建View的线程,即创建View的线程中是否含有此ViewRootImpl
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "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(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
...
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
//关键代码,ViewTree列表
scheduleTraversals();
}
}
上述代码中,进入之后会线程以及重绘区域的检查,之后调用invalidateRectOnScreen方法,然后调用scheduleTraversals()方法。
来继续看看ViewRootImpl#scheduleTraversals()。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// handler消息传递绘制请求
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
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");
}
// 关键代码,执行ViewTree遍历
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
上述代码中,将会之后handler,之后会调用mTraversalRunnable类,从而调用doTraversal方法,最后调用performTraversals()执行ViewTree的遍历。
现在继续查看ViewRootImpl#performTraversals()方法。
private void performTraversals() {
...
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();
}
}
...
}
private void performDraw() {
...
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
// 关键代码
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
...
}
在其中进行View的是否可见,是否为surfasce,是否正在绘制,是否存在于删除列表中等判断,之后调用performDraw()开始执行绘制。在performDraw()又调用了ViewRootImpl的draw方法,并传递了fullRedrawNeeded参数,此参数源自mFullRedrawNeeded成员变量,用于表示是否需要重新绘制全部的View。现在继续看看ViewRootImpl#draw源码。
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
...
// 获取mDirty,该值表示需要重绘的区域
final Rect dirty = mDirty;
if (mSurfaceHolder != null) {
// The app owns the surface, we won't draw.
dirty.setEmpty();
if (animating) {
if (mScroller != null) {
mScroller.abortAnimation();
}
disposeResizeBuffer();
}
return;
}
// 如果为ture,则设置dirty区域为全屏
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
...
// 重绘区域、动画判断
// 硬件渲染判断
// 关键代码
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
...
}
在draw方法中,根据传如fullRedrawNeeded参数,设置需要重绘的dirty区域,最后调用drawSoftware方法,把参数传递进去,现在继续看ViewRootImpl#drawSoftware源码。
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
...
try {
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
// 关键代码,mView为DecorView,开启View绘制
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
}
...
}
上述代码中,首先对canvas进行一些属性设置,包括色块、平移等。之后调用mView.draw(canvas)方法,开始对View进行绘制。mView就是window中的顶级视图DecorView(这个坑会在之后的文章中说明,这里当做一个顶级的ViewGroup即可)。
总结
invalidate会层层向上调用,来合并需要重新绘制的区域,最总在draw的时候绘制。所以我们看到的每一帧画面,有可能并不是整个屏幕的重新绘制,而是绘制变化的区域来更新展现给用户的视图,这样的算法可以给性能带来很大的提升。