Android点五

一、View绘制流程

Android绘制流程详尽版
AndroidView绘制精简版
ViewRootImpl和绘制流程
Android View绘制流程

主流程

Android 渲染流程是面试中的高频考点,核心围绕“应用层绘制 → 系统层合成 → 硬件显示”三个阶段展开,涉及多个关键组件和机制。以下是结构化的面试级解析:

一、核心目标

Android 渲染的核心是将应用的 UI 内容(文字、图像、布局等)高效转换为屏幕上的像素,确保流畅的视觉体验(理想帧率 60 FPS,即每帧渲染耗时 ≤ 16.6ms)。

二、完整渲染流程(分阶段解析)

1. 应用层:UI 绘制(Measure → Layout → Draw)
应用层通过 View 体系完成 UI 内容的计算与绘制,核心是 MLD 流程

  • Measure(测量)
    递归计算每个 View 的宽高(onMeasure()),根据父 View 的约束确定子 View 的尺寸范围(如 match_parent/wrap_content)。

  • Layout(布局)
    确定每个 View 在父容器中的位置(onLayout()),通过坐标(left/top/right/bottom)定位。

  • Draw(绘制)
    将 View 的视觉内容绘制到画布(onDraw(Canvas)),包括:

    • 绘制背景(drawBackground());
    • 绘制自身内容(如 Canvas.drawText()/drawBitmap());
    • 绘制子 View(递归调用子 View 的 draw());
    • 绘制装饰(如滚动条、前景)。

    关键机制

    • Canvas:封装绘制指令的抽象类,提供绘制图形、文本、图像的 API。
    • 硬件加速:Android 3.0+ 支持,将绘制指令转换为 GPU 可执行的操作(如 OpenGL ES 调用),替代 CPU 软件绘制,提升效率。
    • DisplayList:硬件加速下,绘制指令会被缓存为 DisplayList(记录绘制操作序列),避免重复计算(如 View 未变化时直接复用)。

2. 系统层:合成与提交(Surface → SurfaceFlinger)
应用层绘制完成后,内容需提交给系统服务处理:

  • Surface:每个窗口(如 Activity、Dialog)对应一个 Surface,是应用与系统交互的“绘图表面”,内部包含图形缓冲区(GraphicBuffer),用于存储渲染后的像素数据。
  • 提交绘制结果
    应用通过 Surface.lockCanvas() 获取缓冲区,绘制完成后调用 Surface.unlockCanvasAndPost() 提交,缓冲区被传递给 SurfaceFlinger(系统核心服务)。
  • SurfaceFlinger 合成
    SurfaceFlinger 接收所有应用的 Surface(按 Z-order 排序,即窗口层级),将多个 Surface 的像素数据合成到一个屏幕缓冲区(如通过 GPU 的图层混合),最终输出到屏幕。

3. 硬件层:显示输出(VSync 同步)

  • VSync(垂直同步信号)
    屏幕硬件每秒产生 60 次 VSync 信号(60 FPS 场景),触发渲染流程的同步:
    • 应用层通过 Choreographer 监听 VSync 信号,确保在信号到来时启动 MLD 流程,避免“画面撕裂”(前一帧未绘制完就显示下一帧)。
  • 显示硬件
    合成后的屏幕缓冲区数据通过显示控制器(如 LCD 控制器)发送到屏幕,完成像素点亮。
三、关键组件与机制
组件/机制 作用 核心考点
DisplayList 缓存硬件加速下的绘制指令 减少重复绘制,提升渲染效率
Surface 应用的绘图表面,存储像素数据 每个窗口一个 Surface,隔离绘制内容
SurfaceFlinger 系统服务,负责多 Surface 合成 处理窗口层级、透明度、旋转等合成逻辑
VSync 垂直同步信号,同步渲染节奏 避免画面撕裂,控制帧率
硬件加速 用 GPU 替代 CPU 处理绘制 支持复杂绘制,但部分操作不兼容(需禁用)
四、常见问题与优化方向(面试拓展)
  1. 掉帧(FPS 不足 60)的原因

    • 应用层:MLD 流程耗时过长(如复杂布局、过度绘制、主线程阻塞);
    • 系统层:SurfaceFlinger 合成压力大(如过多重叠窗口、大分辨率 Surface)。
  2. 优化手段

    • 减少过度绘制(通过 Layout Inspector 检测,移除不必要的背景);
    • 简化布局(使用 ConstraintLayout 减少层级,避免嵌套过深);
    • 开启硬件加速(默认开启,注意兼容问题);
    • 避免主线程耗时操作(将网络/IO 移到子线程);
    • 复用 DisplayList(减少 View 频繁重绘)。
总结

Android 渲染流程是“应用层绘制(MLD)→ 系统层合成(SurfaceFlinger)→ 硬件显示(VSync 同步)”的完整链路,核心是通过 硬件加速、DisplayList 缓存、VSync 同步 等机制确保高效渲染。面试中需重点掌握各阶段的核心步骤、关键组件作用,以及性能优化思路。

其他:

其它相关问题

1.getWidth()和getMeasuredWidth()的区别

getMeasuredWidth()、getMeasuredHeight()必须在onMeasure之后使用才有效)

getMeasuredWidth() 的取值最终来源于 setMeasuredDimension() 方法调用时传递的参数;

getWidth()与getHeight()方法必须在layout(int l, int t, int r, int b)执行之后才有效

getWidth()返回的是,mRight - mLeft,mRight、mLeft 变量分别表示 View 相对父容器的左右边缘位置;

2.View.post(runnable)原理

利用Handler通信机制,发送一个Runnable到MessageQueue中,当View布局处理完成时,自动发送消息,通知UI进程。借此机制,巧妙获取View的高宽属性,代码简洁,相比ViewTreeObserver监听处理,还不需要手动移除观察者监听事件。

3.requestLayout()的作用

requestLayout()也可以达到重绘view的目的,但是与invalidate和postInvalidate不同,它会先调用onLayout()重新排版,再调用ondraw()方法。

当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view(父类的视图)重新调用他的onMeasure、onLayout来重新设置自己位置。特别是当view的LayoutParams发生改变,并且它的值还没能应用到view上时,这时候适合调用这个方法requestLayout()

4.Window&View

Window&View
①.window是什么?
是所有Viewde直接管理者,任何视图都通过window呈现。Activity的setContentView底层都通过Window完成。
-window是一个抽象类,唯一实现类是PhoneWindow;Window并不是实际存在的,而是以View的形式存在。
-创建Window需要通过WindowManager创建
-实际使用中无法直接访问Window,必须通过WindowManager
-Window具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是通过IPC完成(PC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程,这里指的是Binder)
-Window和View通过ViewRootImpl建立联系
-WMS把所有的用户消息发给View/ViewGroup,但是在View/ViewGroup处理消息的过程中,有一些操作是公共的, Window把这些公共行为抽象出来, 这就是Window。

②.Window类型
FrameWork定义了三种窗口类型,三种类型定义在WindowManager,通过LayoutParams.type设置。
-应用窗口,对应一个Activity,每个Activity都包含一个Window对象。加载Activity由AmS完成,创建一个应用窗口只能在Activity内部完成(层级1~99)。
-子窗口,必须依附于任何类型的父窗口(层级1000~1999)。
-系统窗口,不需要对应任何Activity,如:状态栏,导航栏,普通应用程序不能创建系统窗口,必须要有系统应用权限.(层级2000~2999)。
int值越大层位置越靠上
如下图:

image.png

image.png

3.WindowManger的功能
主要就三个:添加、更改、删除View


image.png

WindowManger是一个接口,ViewManager是其父接口,WindowMangerImpl是其实现类。

4.Activity的setContentView流程
Activity#setContentView()实际通过getWindow().setContentView()交由PhoneWindow处理。
PhoneWindow中主要做两件事:
①当mDecor为空的时候,通过installDecor()初始化DecorView(mDecor)【DecoreView本质就是一个FrameLayout,是Activity中的顶级View】
②generateLayout()实际就是获取布局的父容器(mContentParent), 然后通过inflate将我们的setContentView传入的View或者layout布局文件填充到这个mContentParent,而这个父容器mContentParent实际上就是DecorView的id为R.id.content的子容器。

5.Activity、Window、DecorView、View之间的关系


image.png

DecorView 把屏幕划分为了两个区域:一个是 TitleView,也就是ActionBar或者TitleBar,一个是 ContentView,而我们平时在 Xml 文件中写的布局正好是展示在 ContentView 中。
6.DecorView何时被WindowManager添加到Window中?
ActivityThread的handleResumeActivity方法中,首先会调用Activity的onResume方法,接着调用Activity的makeVisible()方法
makeVisible()中通过WindowManager.addView()完成了DecorView的添加和显示两个过程


image.png

7.Dialog的创建过程:
//TODO

8.Window常见FLAG属性


image.png

image.png

这些属性有几个还有很重要的,比如:
FLAG_KEEP_SCREEN_ON: 屏幕常亮,可以在播放视频的时候设置这个参数
FLAG_FULLSCREEN: 沉浸式全屏
FLAG_SECURE:禁止截屏
FLAG_SHOW_WHEN_LOCKED:窗口在锁屏窗口上显示

activity和window的关系,dialog的window用的是谁的

一、Activity 与 Window 的关系
  1. Activity 依赖 Window 展示界面:每个 Activity 在初始化时(attach() 方法)会创建一个 PhoneWindowWindow 的唯一实现),并通过它承载 UI 布局(如 setContentView 实际由 PhoneWindow 完成)。
  2. Window 是显示与事件载体PhoneWindow 包含根视图 DecorView,负责 UI 渲染和事件传递(通过 Callback 接口将事件交给 Activity 处理)。
  3. 生命周期绑定Activity 的生命周期(如 onResume/onDestroy)会驱动 Window 的创建、显示和销毁。
二、Dialog 的 Window 用的是谁的?

DialogWindow独立创建的(自己实例化 PhoneWindow),但必须依赖宿主 Activity 的窗口令牌(Token)。它与宿主 ActivityWindow 是不同对象,仅通过宿主的上下文关联,确保显示层级和权限正确(如依附于宿主窗口之上)。

synchornized的实现原理

Java 中 synchronized 是实现线程同步的核心关键字,其底层基于 JVM 的对象监视器(Monitor) 机制,经历了 JDK 1.6 的重大优化(从重量级锁升级为分级锁)。面试中需重点掌握其实现原理、锁升级过程及核心机制:

一、核心原理:基于对象监视器(Monitor)

synchronized 的同步语义依赖于 对象监视器(Monitor,也称为管程),每个 Java 对象都天生携带一个 Monitor(由 JVM 实现),其工作原理可概括为:

  • 当线程进入 synchronized 代码块时,需获取对象的 Monitor 所有权
  • 同一时刻只有一个线程能持有 Monitor(互斥性);
  • 其他线程尝试获取时会进入阻塞队列,直到当前线程释放 Monitor。
二、锁的实现形式

synchronized 可修饰方法和代码块,底层通过不同方式关联 Monitor:

  1. 修饰普通方法:锁对象是 当前实例(this),通过方法常量池中的 ACC_SYNCHRONIZED 标志位实现。
  2. 修饰静态方法:锁对象是 当前类的 Class 对象,同样通过 ACC_SYNCHRONIZED 标志位。
  3. 修饰代码块:通过 monitorentermonitorexit 指令实现,锁对象为代码块括号中指定的对象(如 synchronized(obj) { ... } 中的 obj)。
三、JDK 1.6 后的锁升级:从低效到高效

JDK 1.6 前,synchronized重量级锁(直接调用操作系统互斥量,开销大)。优化后引入分级锁机制,根据竞争强度自动升级,减少性能损耗:

  1. 无锁状态:对象刚创建,未被任何线程锁定。
  2. 偏向锁
    • 当只有一个线程访问时,锁会“偏向”该线程,记录线程 ID(通过对象头的 Mark Word 存储)。
    • 后续该线程进入时无需竞争,直接获取锁(避免 CAS 操作开销)。
    • 若有其他线程竞争,偏向锁会升级为轻量级锁。
  3. 轻量级锁
    • 多线程交替访问时,通过 CAS 操作尝试获取锁(将对象头的 Mark Word 替换为线程栈中的锁记录指针)。
    • 若 CAS 成功,线程获取锁;若失败(存在竞争),自旋几次后升级为重量级锁。
  4. 重量级锁
    • 多线程激烈竞争时,依赖操作系统的互斥量(Mutex)实现,未获取锁的线程进入内核态阻塞(开销最大)。
四、关键数据结构:对象头(Mark Word)

锁的状态存储在对象头的 Mark Word 中(32 位 JVM 占 4 字节,64 位占 8 字节),不同锁状态对应不同存储结构:

  • 无锁/偏向锁:存储对象哈希码、分代年龄、偏向线程 ID 等。
  • 轻量级锁:存储指向线程栈中锁记录的指针。
  • 重量级锁:存储指向 Monitor 的指针。
五、核心优势与面试考点
  1. 原子性保证:通过 Monitor 实现临界区资源的独占访问。
  2. 可见性保证:释放锁时会将工作内存数据刷新到主内存,获取锁时会从主内存加载最新数据。
  3. 有序性保证:通过“happens-before”规则限制指令重排序。

高频问题

  • 问:synchronizedvolatile 的区别?
    答:synchronized 保证原子性、可见性、有序性;volatile 仅保证可见性和有序性,不保证原子性。
  • 问:锁升级的意义?
    答:减少无竞争/低竞争场景下的性能开销(偏向锁→轻量级锁避免内核态切换,重量级锁应对高竞争)。
总结

synchronized 底层基于对象 Monitor 实现,JDK 1.6 后通过锁升级(偏向锁→轻量级锁→重量级锁) 优化性能,核心是根据竞争强度动态调整实现方式,平衡同步安全性与效率。面试中需结合对象头、Monitor 机制和锁升级过程阐述,体现对底层原理的理解。

Android事件分发机制
https://cloud.tencent.com/developer/article/1601306

Android问题简单汇总

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容