【Android】View的绘制

流程

当一个 Activity 进入可见状态并即将显示在屏幕上时,会触发以下操作过程

onResume 方法

Activity 的生命周期方法 onResume() 被调用,在该方法内部可以进行一些准备工作,因为此时 Activity 即将显示给用户。

激活 Instrumentation

在 onResume 方法中,会激活 Instrumentation 实例,通过调用它的 callActivityOnResume 方法来通知 Activity 即将进入 Resume 状态

WindowManager 添加 DecorView

在 callActivityOnResume 方法中,会调用 WindowManager 的 addView 方法,将 DecorView 添加到 Window 上。WindowManager 是 Android 系统负责管理窗口的类。

创建 ViewRootImpl

当调用 WindowManager 的 addView 方法时,会创建一个 ViewRootImpl 对象。ViewRootImpl 是视图层级的根节点,负责管理和绘制整个视图层级。

设置 DecorView

通过调用 ViewRootImpl 的 setView 方法,将 DecorView 设置为视图层级的根视图。

添加到集合

在添加 DecorView 到 Window 上后,还会将 DecorView、ViewRootImpl 和 WindowManager.LayoutParams 等对象添加到相应的集合中,以供后续使用和管理。

绘制和显示

随着 DecorView 的添加和设置完成,ViewRootImpl 开始处理绘制操作。它负责测量每个 View 的大小、布局各个 View 的位置,并在接收到绘制指令后进行实际的绘制工作。

其中关于DecorView的设置

在 Activity 中使用 setContentView 方法设置布局文件,例如 setContentView(R.layout.activity_main)。该方法会将指定的布局文件解析为 View,并将其设置为当前 Activity 的主视图。

Activity 的 setContentView 方法内部会通过getWindow().setContentView()将解析得到的 View 添加到一个名为DecorView的容器中,即装饰视图。DecorView 是整个 Activity 窗口的最顶层容器,用于承载所有的用户界面元素。

当解析布局文件时,XML 中定义的各种 View(如 TextView、ImageView 等)以及它们的属性会被转化为相应的对象,然后按照 XML 中的层级关系进行构造。最终,这些 View 会被添加到 DecorView 中,成为整个界面的一部分。

Measure(测量):在 View 添加到 DecorView 后,系统会自动触发测量过程。测量会根据 View 的布局属性和父容器的大小等信息来确定每个 View 所占据的空间大小。

Layout(布局):测量完成后,系统会根据测量结果计算出每个 View 的位置和尺寸,并将其布局到屏幕上的合适位置。

Draw(绘制):布局完成后,系统会调用每个 View 的绘制方法来进行实际的绘制操作。View 的绘制过程一般包括填充背景、绘制内容(如文本、图片等)、处理触摸事件等。

管理和显示窗口的三个重要的类

WindowManagerImpl:

是 WindowManager 的实现类之一,它负责与系统窗口服务进行通信,将视图添加到特定的屏幕和父窗口中。WindowManagerImpl 主要负责管理窗口的显示、更新和移除等操作,通过调用系统底层的窗口管理接口来完成这些操作。

WindowManagerGlobal:

是 WindowManagerImpl 的全局管理器,负责跟踪管理整个进程中的所有窗口信息。它维护了一个窗口集合(mViews)、ViewRootImpl 集合(mRoots)和 WindowManager.LayoutParams 集合(mParams),用于存储和管理所有窗口的相关信息。

ViewRootImpl:

是每个窗口的根视图,负责处理窗口的布局、绘制和事件分发等操作。ViewRootImpl 与具体的 Window 绑定,通过 ViewRootImpl 可以获取到 Window 的根视图,即 DecorView。它是 WindowManagerGlobal 实际操作窗口的执行者,调用具体的绘制和事件处理方法。

总结起来,WindowManagerImpl WindowManager 接口的实现类,负责具体的窗口管理操作;WindowManagerGlobal 则是 WindowManagerImpl 的全局管理器,负责跟踪和管理所有窗口信息;而 ViewRootImpl 则是每个窗口的根视图,负责窗口布局、绘制和事件处理等。它们三者之间通过相互调用和协作,实现了在 Android 中有效管理和显示窗口的功能。

UI刷新一定要在主线程吗?

不是

在通常情况下,UI刷新的操作应该在主线程进行。这是因为Android的UI框架是线程不安全的,只能在主线程(也称为UI线程)中更新UI相关的操作,例如修改视图的属性、调用View的绘制方法等。这是为了确保多个线程不会同时对UI进行修改,从而避免可能的并发问题和不一致性。

UI刷新是由ViewRootImpl负责的,在ViewRootImpl里面刷新不管是调用requestLayout()还是调用View.invalidate()进行页面的刷新,最后都会调用checkThread()方法进行线程的检测,当线程和ViewRootImpl线程一致时可以进行页面的刷新,而ViewRootImpl是在主线程下面创建的,所以才有了这个在主线程刷新UI的规定。

然而,如果确实有在子线程中刷新UI的需求,可以采取以下两种方式:

在ViewRootImpl创建之前调用:

通过在ViewRootImpl实例创建之前修改相关参数,可以使得后续的UI刷新操作在子线程中执行。但是这种方法需要对Android的内部机制有深入的了解,并且可能会导致不可预测的结果,不建议在正式项目中使用。

在需要刷新UI的子线程创建ViewRootImpl:

在子线程中创建一个新的ViewRootImpl实例,并将其绑定到当前线程的Looper上。然后使用该ViewRootImpl实例来进行UI刷新操作。这种方式较为常见,一般是通过Handler或AsyncTask等方式,在子线程中发送消息或执行后台任务,然后在相应的处理方法中进行UI刷新操作。

需要注意的是,在子线程中进行UI刷新可能会引发一些问题,例如视图状态更新不及时、线程同步、资源消耗等。因此,在使用子线程刷新UI时,需要慎重考虑并合理处理相关问题。

猜你喜欢

转载自blog.csdn.net/qq_43358469/article/details/131910218
今日推荐