一张图带你了解 View 的绘制流程
2025-2-5
| 2025-3-13
Words 2412Read Time 7 min
type
status
date
slug
summary
tags
category
icon
password

前言

关于 Android View 绘制流程是个老生常谈的主题了,相关的优秀博客也有很多,本篇博客希望通过一张时序图作为主线,尽量以好记、好看、好讲的方式简要表述 View 绘制流程一些关键节点。

开发环境

View 绘制流程时序图

notion image
 

对象(Object)描述

对象
描述
ActivityThread
负责应用程序的生命周期管理和主线程消息循环。
Activity
• 提供用户界面:每个 Activity 都包含一个布局文件(XML)或动态生成的视图(View),用于显示内容。 • 处理用户交互:响应用户操作,如点击、滑动等。 • 管理生命周期:Activity 有自己的生命周期,系统会在不同状态下回调相应方法(如创建、启动、暂停、销毁等)。 • 组件通信:通过 Intent 与其他 Activity、Service 或应用组件通信。
PhoneWindow
PhoneWindow 是 Window 类的具体实现,用于表示一个应用程序窗口。它是 Activity 和 Dialog 的窗口实现类,负责管理窗口的装饰(DecorView)和内容视图。
DecorView
它是整个应用窗口 View 树,触摸事件和其他输入都会首先到达这里,然后再分发下去。
ViewRootImpl
负责 View 的测量、布局、绘制、渲染,事件分发和与 WindowManagerService 的交互。
WindowManagerImpl
WindowManagerImpl 是 WindowManager 接口的具体实现类,负责窗口的添加、更新和删除等操作。它直接与应用程序交互,处理窗口的布局、绘制和事件分发。每个 Activity 或 Dialog 通常都有一个对应的 WindowManagerImpl 实例,管理其窗口。
WindowManagerGlobal
WindowManagerGlobal 是一个全局单例类,负责管理整个应用程序的所有窗口。它协调多个 WindowManagerImpl 实例,确保窗口操作的一致性和同步。作为系统级窗口管理的核心,与 WindowManagerService 交互,执行窗口的添加、更新和删除等操作。

消息(Message)说明

① handleLaunchActivity()
Binder + Handler 消息机制,ApplicationThread 接收到跨进程的消息,通过 Handler (H) 将其切换到 ui 线程(ActivityThread),第一个消息是 LaunchActivityItem(LAUNCH_ACTIVITY),它调用 handleLaunchActivity(),在这个方法里完成了 Activity 的创建和启动等。
② performLaunchActivity()
 
⑧ setContentView()
把用户设置的 setContentView(resId) 布局添加到 DecorView 布局下
⑨ installDecor()
⑪ handleResumeActivity()
Binder + Handler 消息机制,触发 ActivityThread 的 handleResumeActivity()
⑭ new ViewRootImpl(
⑮ root.setView()
⑰ scheduleTraversals()
 
㉑ performTraversals()
屏幕的下一个 Vsync 信号到来,⑰ 设置的下一帧回调触发,执行到 performTraversals()
 

常见问题

为什么我在 onCreate() 中调用 View.post() 方法可以得到 View 的宽高呢?
onCreate() 时 mAttachInfo 为 null,任务放到了消息队列中,不是立刻执行,等到 Activity::onResume(),dispatchAttachedToWindow() 方法中把 View 的 post 任务添加到 ViewRootImpl handler 中,在 DocerView 经过 measure、loayout、draw 后,任务才执行。
MeasureSpec 的理解
invaliate() 和 requestlayout() 方法的区别?
ViewRootImpl 持有 DecorView 管理 View 的绘制。所以简单来说,requestlayout 和 invaliate 最终都会向上回溯调用到 ViewRootImpl 的 postTranversals 方法来绘制 View。 不同的是 requestlayout 会绘制 View 的 measure,layout 和 draw 过程。invaliate 因为只添加了绘制 draw 的标志位,只会绘制 draw 过程。
Activity 首次启动 onMeasure() 执行两次?
㉑ performTraversals() 方法内执行两次 onMeasure()
  1. 首次测量
      • 这个阶段的主要目的是为了确定 RootView 的尺寸,进而决定 Window 尺寸。
  1. 二次测量
      • 在这个阶段,由于已经知道了 Window 的大小,系统会对整个视图树中的各个组件进行精确地重新测量。
子线程刷新 UI 的几种方式?
  1. 避开 ViewRootImpl::checkThread()检测
      • 利⽤硬件加速机制一些场景可以绕开 requestLayout()
      • 主线程刚刚更新某个 View 后子线程立刻更新
  1. 尝试子线程中创建 ViewRootImpl
  1. GlSurfaceView 也算是在子线程中刷新 UI
 

参考文档

 
后记:View 绘制流程的代码主要在 Java 层,调试起来非常方便。然而,如果在梳理过程中试图涵盖每段代码的细节,就像测量海岸线的长度一样,会陷入无止境的分形中。普通的开发者总是渴望洞悉所有细节,但优秀的开发者懂得何时忽略不必要的细节。
后记:View 绘制流程的代码主要在 Java 层,调试起来非常方便。然而,如果在梳理过程中试图涵盖每段代码的细节,就像测量海岸线的长度一样,会陷入无止境的分形中。普通的开发者总是渴望洞悉所有细节,但优秀的开发者懂得何时忽略不必要的细节。
 
  • Android
  • Android MeasureSpec 简介多窗口页面管理
    Loading...