-
只改内容/样式(不改大小位置) ->
invalidate() -
改了大小/位置/层级 ->
requestLayout() -
postInvalidate ()在子线程的时候调用,(本质是通过 Handler 向主线程发送消息,最终调用 invalidate ())
invalidate():
一、先进行基础校验
1、检查View的状态,若 View 不可见(如 visibility 为 GONE/INVISIBLE)、正在执行动画,或未完成初始化(无 AttachInfo),直接跳过后续流程,不触发任何操作
2、校验调用线程:必须在 UI 线程调用,若在子线程需使用 postInvalidate ()(本质是通过 Handler 向主线程发送消息,最终调用 invalidate ())
二、标记“脏区域”(确定重绘区域)
-
核心逻辑:invalidate () 本质是调用内部的 invalidateInternal () 方法,标记当前 View 的 “脏区域”(即需要重绘的区域);
-
区域区分:
- 无参 invalidate ():标记整个 View 为脏区域,触发全量重绘;
- 重载方法(invalidate (Rect dirty) /invalidate (int l, int t, int r, int b)):仅标记指定矩形区域为脏区域,实现局部重绘,优化性能;
-
缓存处理:标记 View 的绘制缓存(drawing cache)失效,避免使用旧缓存导致显示异常,若无需清空缓存,可通过 invalidate (false) 跳过缓存失效步骤。
三、向上递归传递重绘请求(直至根节点)
标记脏区域后,不会立即绘制,而是将重绘请求沿 View 树向上传递:
-
从当前 View 开始,调用父容器(ViewGroup)的 invalidateChild () 方法,父容器会计算子 View 在自身坐标系下的脏区域,再继续向上传递;
-
最终传递至 ViewRootImpl(整个视图树的根节点),完成重绘请求的调度注册,此过程中各级父 View 会合并脏区域,减少重复绘制开销。
-
最终会调用到 // 最终触发调度遍历,scheduleTraversals();
void scheduleTraversals() { // 防抖机制,可以避免在同一帧时间内多次调用invalidate(),也只会执行一次。 if (!mTraversalScheduled) { mTraversalScheduled = true; // 设置同步屏障,主线程将暂时停止处理普通的 Handler 消息(如点击事件、日志、业务逻辑), // 直到绘制完成后移除屏障。这确保了 UI 渲染消息一旦到来,能立刻获得 CPU 执行权。 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 注册 VSYNC 信号回调 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); // 通知渲染引擎 notifyRendererOfFramePending(); // 唤醒系统 // 此方法可以维持高能耗状态,确保这一帧能平滑地画出来,而不会因为系统降频导致掉帧 pokeDrawLockIfNeeded(); } }
流程总结
将整个 View.invalidate() 到 ViewRootImpl.scheduleTraversals() 的过程串联起来,其本质是一次请求的向上溯源再向下派发绘制信号的调度机制。
向上溯源:不停地去找父View直到找到ViewRootImpl。
向下派发:通过VSYNC信号执行 View树的测量、布局和绘制操作。
步骤
组件
行为
1. 发起
View
调用 invalidate()。
2. 冒泡
ViewGroup
标记 PFLAG_DIRTY,向上寻找 ViewRootImpl。
3. 调度
ViewRootImpl
执行 scheduleTraversals()。
4. 屏蔽
MessageQueue
插入 同步屏障,拦截普通消息。
5. 等待
Choreographer
等待硬件 VSYNC 信号。
6. 回调
mTraversalRunnable
信号到达,执行 performTraversals()。
以下区域可以不用看
引用
在 Android 中,view.invalidate() 是触发界面重绘的核心方法。它的调用链是一个从 子 View 向上溯源至 ViewRootImpl,再向下派发绘制信号 的过程
- 当调用
invalidate()时,最终会进入invalidateInternal方法,并进行“脏区域”的标记 ViewGroup作为父容器,负责计算子 View 在父容器坐标系下的失效区域,并继续往上传递(这里分别有两条绘制路径:硬件绘制路径和软件绘制路径)