UI核心 - UI绘制流程及原理

1. View添加到屏幕窗口的源码分析

入口:在Activity的onCreate中调用setContentView传入布局资源ID。 接着调用 :getWindow().setContentView(layoutResID); 其中 getWindow,返回就 Window 的唯一实现类 PhoneWindow。

java 复制代码
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

PhoneWindow:

java 复制代码
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            ...
        }
        ...
    }
java 复制代码
    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1); // return new DecorView 
           ...
        } else {
            ...
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ...
        }
    }

在 PhoneWindow 的 setContentView 中,会调用 installDecor 里面的方法执行 generateDecor 创建 DecorView。 DecorView继承自FrameLayout,是应用窗口的顶层View容器。 然后将 mDecor 传进 generateLayout, 该方法根据系统主题属性(如是否有ActionBar、浮动窗口等)确定不同的layoutResource(例如R.layout.screen_simple)。 通过 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 将得到的布局资源添加到 DecorView 中。 然后通过 findViewById 获取 id 为 com.android.internal.R.id.content 的基础布局,一般为 FrameLayout,后面我们的界面就是绘制在这个布局里面的。 最终返回这个 contentParent

小结

PhoneWindow持有DecorView。DecorView中包含根据主题生成的基础布局(如screen_simple),该基础布局通常包含标题栏(Title/ActionBar)和内容区域(ID为content的FrameLayout)。我们的XML布局最终被添加到这个content容器中。

2. View绘制流程的触发机制

绘制的入口是ActivityThread的handleResumeActivity方法。 该方法在执行performResumeActivity(回调onResume)后,初始化WindowManager.LayoutParams,并调用WindowManager的addView方法。

WindowManager 来源:

  • Activity中的WindowManager接口实现类为WindowManagerImpl。
  • WindowManagerImpl的addView方法委托给WindowManagerGlobal的addView方法。
  • WindowManagerGlobal.addView中创建ViewRootImpl对象,并将DecorView、布局参数等与之关联。

关键调用:root.setView(view, wparams, panelParentView),其中root即为ViewRootImpl实例。

3. 绘制核心流程:PerformTraversals

在ViewRootImpl.setView中调用requestLayout()。该方法首先通过checkThread()确保当前操作在主线程执行,随后调用scheduledTraversals()。 scheduledTraversals()通过Choreographer(编舞者) post一个TraversalRunnable。当VSync信号到来时,TraversalRunnable的run方法被执行,进而调用doTraversal()。 doTraversal()的核心是调用performTraversals()。在此方法中,依次执行UI绘制的三个关键步骤:

  • 测量 (Measure):调用performMeasure(),确定View的大小。
  • 布局 (Layout):调用performLayout(),确定View的位置。
  • 绘制 (Draw):调用performDraw(),将View内容渲染到屏幕上。
相关推荐
传奇开心果编程1 小时前
【Flutter踩坑排雷】第19课:路由硬编码坑|字符串路由书写错误,页面跳转失败排查
android·flutter·ios·移动开发·跨平台·ui框架·声明式ui
微小的鱼4 小时前
Linux的渲染与显示系统链路
android·java·linux
道尔柯南15 小时前
C++ string 类详解:从常用接口到深拷贝,彻底掌握字符串处理
android·java·c++
唐诺16 小时前
5、Android JNI 动态注册中的“签名“详解
android·jni
zhaoshuzhaoshu20 小时前
Android 系统启动与调试中的日志抓取方式全解析
android
雨声不在21 小时前
android 动画掉帧
android
在多学一点1 天前
MYSQL第二次作业
android·数据库·mysql
骑着蜗牛撵大象3271 天前
MySQL 8.0 迁移后 SQL 变慢:执行计划里 3 类被忽略的成本差异
android·数据库·sql·mysql·优化器·执行计划
我叫张土豆1 天前
Android 14+ 模拟器抓包实录:把 Burp CA 装成系统 CA,让 targetSdk≥34 的 App 乖乖解密
android
booboosui1 天前
Android 悬浮窗藏不住自己:一个标志位与 140 毫秒
android