背景
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














网友评论