背景
Invalidate()
是 Android
中 View
的方法,通常我们使用它来完成UI
的刷新,
作用
如果这个 View
可见那么 onDraw()
方法将在未来某个时间点被调用。
问题
invalidate()
会触发那些 view
的重绘,invalidate()
绘制流程是如何实现的?
我们带着问题来从源码开始分析:
源码分析
一、View 与 ViewGroup 的层级
在 Android
中 View
是以树形结构组织的,下图相信大家都不陌生,那么我们调用红色View的 invalidate()
会发生什么?
1.1 View.invalidate()
方法入口:
public void invalidate() {
invalidate(true);
}
参数:
-
l,t,r,b
是 View 的大小 -
invalidateCache
: 设置 View 的缓存是否失效,通常情况下是ture
, 当View
的大小改变时为false
-
fullInvalidate
: 默认为true
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
...
final AttachInfo ai = mAttachInfo;
// 改 View 的父布局
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
// 记录需要绘制的范围 damge ,该区域为 View 尺寸
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
// 调用父布局的 invalidateChild()
p.invalidateChild(this, damage);
}
...
}
说明: 将 View
需要绘制大小 Rect
告诉父ViewGroup
, 并调用父 ViewGroup
的 invalidateChild()
1.2 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) {
...
// 这个循环会一直找到父布局的 DecordView invalidateChildInParent()
do {
...
// 标记 View
if (view != null) {
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
}
}
parent = parent.invalidateChildInParent(location, dirty);
...
} while (parent != null);
}
}
绘制分为两个分支:
- 硬件加速绘制
- 软件绘制
硬件加速绘制不做介绍,主要分析软件绘制。
软件绘制会循环一直到根的 DecorView
中,而 DecorView
是由 ViewRootImp
管理,并维护 mPrivateFlags
mPrivateFlags:
计算需要刷新的View
1.3 ViewRootImp.invalidateChildInParent()
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
...
invalidateRectOnScreen(dirty);
return null;
}
在根布局 ViewRootImp
会处理我们传入的 Rect dirty
区域。
1.4 ViewRootImp.invalidateRectOnScreen()
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
// 通过我们传入的区域,计算需要更新的区域
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
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();
}
}
通过我们 dirty
的区域,计算需要更新的区域,然后调用 scheduleTraversals()
。
1.5 ViewRootImp.scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
通过 Handler
开启同步屏障, Post
一个 Callback
,接下来我们来看这个 Callback
的实现。
1.6 ViewRootImp.mTraversalRunnable
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
主要记录绘制时间,并调用 performTraversals()
开始绘制
1.7 核心函数 ViewRootImp.performTraversals()
void performTraversals() {
if (layoutRequested) {
// Clear this now, so that if anything requests a layout in the
// rest of this function we will catch it and re-run a full
// layout pass.
mLayoutRequested = false;
}
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
...
}
依次执行 performMeasure(),performLayout(),performDraw()
mLayoutRequested:
默认为 false
,意味着只会执行 onDraw()
, 不会执行 onMeasure()
1.8 performMeasure()
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
调用 View.measure()
。
1.9 View.measure()
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if (forceLayout || needsLayout) {
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
...
}
}
view.measure()
会调用我们重写的 onMeasure()
。
View.layout()
与 View.draw()
方法与 View.measure()
方法执行流程类似不在分析
总结:
invalidate() 整体流程:
Android
中 UI
刷新的逻辑都是按照以上树形结构来运行的,从子 View
把要做的事情传给根布局,再由根布局分布整个 View Tree
, 导致整个 View Tree
进行刷新。
invalidate() 性能考虑:
- 子
View
将更新逻辑传递给ViewRootImp
过程中,通过Rect
计算需要更新的View
,并用mPrivateFlags
标记出来。 -
View
从上往下执行onMeasure()、onLayout()、onDraw()
时通过mPrivateFlags
判断是否刷新达到优化的目的
invalidate只是调用 performDraw()
onMeasure()、onLayout()、onDraw()
这三个函数不一定都执行,由 mLayoutRequested
默认是 false
仅会执行 onDraw()
。
如果发现
onMesure()、onLayout()
无效时候,通常我们会调用requestLayout()
, 本质上它是将mLayoutRequested
设置为true
网友评论