一、視圖系統(tǒng)核心組件與職責(zé)
1.1 Window 體系
-
Window 是一個(gè)抽象類,通過控制
DecorView提供標(biāo)準(zhǔn) UI 方案,例如:- 背景
- 標(biāo)題
- 虛擬按鍵等
-
PhoneWindow 是
Window的唯一實(shí)現(xiàn)類,其作用包括:- 完善
Window的功能; - 提供事件的中轉(zhuǎn)。
- 完善
【注意】
PhoneWindow只是提供標(biāo)準(zhǔn) UI 方案,與窗口不等價(jià)。
1.2 WindowManager 體系
WindowManager 是一個(gè)接口,繼承自
ViewManager接口,提供 View 的基本操作方法(如addView、removeView、updateViewLayout)。WindowManagerImpl 實(shí)現(xiàn)了
WindowManager接口,其內(nèi)部通過組合方式持有WindowManagerGlobal,用于實(shí)際操作 View。-
WindowManagerGlobal 是一個(gè)全局單例,其核心能力包括:
- 內(nèi)部可通過
ViewRootImpl將 View 添加至窗口中。
- 內(nèi)部可通過
1.3 ViewRootImpl —— 視圖系統(tǒng)的樞紐
-
ViewRootImpl 是所有 View 的 Parent(邏輯上的根節(jié)點(diǎn)),其核心職責(zé)包括:
- 管理 View 的繪制流程;
- 負(fù)責(zé)窗口的開辟;
- 是視圖系統(tǒng)最關(guān)鍵的一環(huán)。
-
IWindowSession:
- 是
IWindowSession類型的 AIDL 接口; - 可通過 Binder IPC 通知 WMS(WindowManagerService)開辟窗口。
- 是
【補(bǔ)充】
盡管開發(fā)者日常只接觸Activity.setContentView(),但錯(cuò)綜復(fù)雜的視圖系統(tǒng)基本都隱藏在Activity內(nèi)部,開發(fā)者只需基于模板方法即可開發(fā)。
1.4 Android 視圖體系的本質(zhì)理解
在深入技術(shù)細(xì)節(jié)前,需明確 Android 視圖系統(tǒng)的分層抽象邏輯。
一切視圖均由
Canvas而來
所有 UI 元素的繪制最終都通過Canvas的繪圖指令(如drawRect,drawText)完成;Canvas是連接應(yīng)用邏輯與 GPU 渲染的橋梁。View的出現(xiàn)是為了提供視圖模板,用來提升開發(fā)效率
若無View,開發(fā)者需手動(dòng)管理每個(gè)像素的繪制、事件分發(fā)、布局計(jì)算。View封裝了通用行為(measure/layout/draw/事件處理),形成可復(fù)用的組件模型。窗口可以讓
View有條不紊地顯示
“窗口”是操作系統(tǒng)層面的概念,代表一塊獨(dú)立的屏幕區(qū)域。Android 通過Window抽象將 View 樹與底層顯示服務(wù)(SurfaceFlinger)關(guān)聯(lián),確保多個(gè) App 的界面互不干擾。Activity給每個(gè)窗口增加生命周期,讓窗口切換更加優(yōu)雅
Activity不僅承載 UI,更通過onCreate/onResume/onPause等回調(diào)協(xié)調(diào)窗口的創(chuàng)建、激活、暫停與銷毀,實(shí)現(xiàn)多任務(wù)間平滑過渡。PhoneWindow只是提供些標(biāo)準(zhǔn)的 UI 方案,與窗口不等價(jià)
PhoneWindow是Window的具體實(shí)現(xiàn),負(fù)責(zé)生成包含狀態(tài)欄、標(biāo)題欄、內(nèi)容區(qū)的DecorView結(jié)構(gòu),但它本身不是窗口實(shí)體,僅是 UI 模板提供者。可通過
WindowManager將View添加到窗口
WindowManager是客戶端接口,調(diào)用其addView()可將任意View(如 Dialog、Toast)附加到當(dāng)前窗口的視圖樹中。-
ViewRootImpl才是開辟窗口的那個(gè)角色,并管理 View 的繪制,是視圖系統(tǒng)最關(guān)鍵的一環(huán)
ViewRootImpl負(fù)責(zé):- 通過
IWindowSession向 WMS(WindowManagerService)申請(qǐng)窗口; - 建立
Surface與Canvas的綁定; - 驅(qū)動(dòng) View 的 measure/layout/draw 三大流程;
- 處理輸入事件分發(fā)。
【補(bǔ)充】
沒有ViewRootImpl,DecorView僅是一棵內(nèi)存中的 View 樹,無法顯示在屏幕上。 - 通過
錯(cuò)綜復(fù)雜的視圖系統(tǒng)基本都隱藏在
Activity內(nèi)部,開發(fā)者只需基于模板方法即可開發(fā)
開發(fā)者通常只調(diào)用setContentView()和重寫生命周期方法,而Activity內(nèi)部自動(dòng)完成PhoneWindow創(chuàng)建、DecorView構(gòu)建、ViewRootImpl關(guān)聯(lián)等復(fù)雜流程。
【補(bǔ)充類比】
可將整個(gè)體系類比為“舞臺(tái)劇”:
Canvas是畫筆;View是演員(自帶臺(tái)詞和動(dòng)作腳本);Window是舞臺(tái)框架;PhoneWindow是舞臺(tái)布景師(提供標(biāo)準(zhǔn)背景板);Activity是導(dǎo)演(控制演出節(jié)奏);ViewRootImpl是舞臺(tái)監(jiān)督(協(xié)調(diào)演員上臺(tái)、燈光、音效);SurfaceFlinger是最終的放映機(jī)。
二、View 的工作原理
2.1 setContentView 與 DecorView 創(chuàng)建
-
當(dāng)調(diào)用
Activity的setContentView()方法后:- 會(huì)調(diào)用
PhoneWindow類的setContentView方法; -
PhoneWindow是抽象類Window的實(shí)現(xiàn)類; -
Window類用于描述 Activity 視圖最頂端的窗口顯示和行為操作。
- 會(huì)調(diào)用
-
在
PhoneWindow的setContentView方法中:- 最終會(huì)生成
DecorView對(duì)象; -
DecorView是PhoneWindow的內(nèi)部類。
- 最終會(huì)生成
2.2 ViewRootImpl 的紐帶作用
-
ViewRoot 的實(shí)現(xiàn)類是
ViewRootImpl; - 它是連接
WindowManager和DecorView的紐帶; - View 的三大流程(measure、layout、draw)均通過
ViewRoot完成。
- 在
ActivityThread中:- 當(dāng) Activity 對(duì)象創(chuàng)建完畢后,會(huì)將
DecorView添加到 Window 中; - 同時(shí)創(chuàng)建
ViewRootImpl對(duì)象; - 并將
ViewRootImpl與DecorView建立關(guān)聯(lián)。
- 當(dāng) Activity 對(duì)象創(chuàng)建完畢后,會(huì)將

2.3 MeasureSpec 機(jī)制
MeasureSpec 是一個(gè) 32 位的 int 值,用于在父容器與子 View 之間傳遞測(cè)量約束。其結(jié)構(gòu)如下:
| 位段 | 長(zhǎng)度 | 含義 |
|---|---|---|
| 高 2 位 | 2 bits |
SpecMode:測(cè)量模式 |
| 低 30 位 | 30 bits |
SpecSize:測(cè)量尺寸(單位:像素) |
可通過 MeasureSpec.makeMeasureSpec(size, mode) 構(gòu)造,或通過 MeasureSpec.getMode(measureSpec) / MeasureSpec.getSize(measureSpec) 解析。
2.3.1 三種 SpecMode 詳解
| Mode | 含義 | 對(duì)應(yīng) LayoutParams | 行為說明 |
|---|---|---|---|
EXACTLY |
精確模式 |
match_parent 或 具體數(shù)值(如 100dp) |
父容器已確定子 View 的確切大小,子 View 必須使用該尺寸。 |
AT_MOST |
至多模式 | wrap_content |
子 View 可以在其最大允許范圍內(nèi)自由決定尺寸,但不能超過 SpecSize。 |
UNSPECIFIED |
未指定模式 | ——(系統(tǒng)內(nèi)部使用) | 父容器不對(duì)子 View 施加任何限制,子 View 可按需返回任意尺寸(常用于 ScrollView 內(nèi)部測(cè)量)。 |
【注意】
UNSPECIFIED模式極少在普通布局中出現(xiàn),主要由系統(tǒng)組件(如ListView、RecyclerView的 item 測(cè)量)使用。
2.3.2 DecorView 的 MeasureSpec 如何確定?
DecorView 作為頂級(jí) View,其 MeasureSpec 不由父容器決定,而是由窗口尺寸和自身的 LayoutParams 共同計(jì)算得出。
窗口尺寸來源:
由 WMS 在relayoutWindow()中分配,反映當(dāng)前 Activity 窗口的實(shí)際可用區(qū)域(扣除狀態(tài)欄、導(dǎo)航欄等)。-
計(jì)算規(guī)則(簡(jiǎn)化):
// 假設(shè)窗口寬高為 windowWidth, windowHeight // DecorView 的 LayoutParams 為 lp int widthMeasureSpec = (lp.width == MATCH_PARENT) ? MeasureSpec.makeMeasureSpec(windowWidth, EXACTLY) : (lp.width >= 0) ? MeasureSpec.makeMeasureSpec(lp.width, EXACTLY) : MeasureSpec.makeMeasureSpec(windowWidth, AT_MOST);
2.3.3 普通 View 的 MeasureSpec 如何確定?
對(duì)于非頂級(jí) View,其 MeasureSpec 由父容器的 MeasureSpec 和自身的 LayoutParams 共同決定。Android 提供了 getChildMeasureSpec() 方法封裝此邏輯。
getChildMeasureSpec 規(guī)則表(父容器 mode × 子 View LayoutParams)
| 父容器 SpecMode | 子 View LayoutParams | 子 View MeasureSpec Mode | 子 View MeasureSpec Size |
|---|---|---|---|
EXACTLY |
match_parent |
EXACTLY |
父容器 size |
EXACTLY |
具體數(shù)值(如 100dp) | EXACTLY |
指定數(shù)值 |
EXACTLY |
wrap_content |
AT_MOST |
父容器 size |
AT_MOST |
match_parent |
AT_MOST |
父容器 size |
AT_MOST |
具體數(shù)值 | EXACTLY |
指定數(shù)值 |
AT_MOST |
wrap_content |
AT_MOST |
父容器 size |
UNSPECIFIED |
match_parent |
UNSPECIFIED |
0 |
UNSPECIFIED |
具體數(shù)值 | EXACTLY |
指定數(shù)值 |
UNSPECIFIED |
wrap_content |
UNSPECIFIED |
0 |
【補(bǔ)充】
此表解釋了為何wrap_content在EXACTLY父容器中表現(xiàn)為“最多占滿”,而在AT_MOST中同樣受限于父容器剩余空間。
2.3.4 MeasureSpec 與 LayoutParams 的映射關(guān)系
| LayoutParams.width | 對(duì)應(yīng) MeasureSpec Mode |
|---|---|
MATCH_PARENT (-1) |
EXACTLY(若父容器為 EXACTLY)或 AT_MOST(若父容器為 AT_MOST) |
WRAP_CONTENT (-2) |
AT_MOST(若父容器為 EXACTLY 或 AT_MOST) |
| 具體數(shù)值(≥0) | EXACTLY |
【補(bǔ)充】
MATCH_PARENT和WRAP_CONTENT是ViewGroup.LayoutParams中的常量,分別對(duì)應(yīng)-1和-2。
2.4 自定義 View 的 onMeasure 實(shí)現(xiàn)規(guī)范
當(dāng)自定義 View 直接繼承 View(而非 TextView、ImageView 等已有實(shí)現(xiàn))時(shí),必須重寫 onMeasure(),否則 wrap_content 將失效。
正確實(shí)現(xiàn)示例:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width;
int height;
// 處理寬度
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize; // 父容器已指定確切大小
} else {
// 計(jì)算內(nèi)容所需寬度(例如文本寬度 + padding)
int desiredWidth = calculateDesiredWidth();
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(desiredWidth, widthSize); // 不能超過父容器限制
} else { // UNSPECIFIED
width = desiredWidth;
}
}
// 高度同理...
setMeasuredDimension(width, height);
}
【關(guān)鍵提醒】
若未處理AT_MOST模式,直接setMeasuredDimension(desiredWidth, desiredHeight),則當(dāng)布局中使用wrap_content時(shí),View 會(huì)無視父容器限制,可能導(dǎo)致布局溢出或性能問題。
2.5 View 測(cè)量與 Activity 生命周期不同步問題
- View 的 measure 過程和 Activity 的生命周期實(shí)際上是不同步的;
- 若在 View 尚未測(cè)量完畢時(shí)獲取寬高,結(jié)果為 0。
解決方案(四種方法):
Activity/View#onWindowFocusChangedview.post(runnable)ViewTreeObserverview.measure(int widthMeasureSpec, int heightMeasureSpec)
2.6 getWidth() / getHeight() 與測(cè)量寬高的關(guān)系
- 默認(rèn)情況下,
getWidth()、getHeight()返回的值正好等于 View 的測(cè)量寬/高; -
形成時(shí)機(jī)差異:
- 測(cè)量寬/高:形成于
measure過程; - 最終寬/高:形成于
layout方法中。
- 測(cè)量寬/高:形成于
-
特殊情況:
- 若在
layout過程中不按默認(rèn)套路出牌(即不使用mMeasuredWidth和mMeasuredHeight,而是人為設(shè)定值),則兩者不相等。
- 若在
三、Android 渲染架構(gòu)
3.1 整體架構(gòu)概覽

-
圖像生產(chǎn)者(Producer):
- 即我們的 App;
- 更底層是
Canvas → Surface。
-
圖像消費(fèi)者(Consumer):
-
SurfaceFlinger。
-
-
圖像緩沖區(qū):
-
BufferQueue,一般采用三緩沖區(qū)機(jī)制。
-
3.2 圖像生產(chǎn)者分類
主要圖像生產(chǎn)者包括:
- MediaPlayer
- CameraPreview
- NDK (Skia)
- OpenGL ES
工作方式差異:
-
MediaPlayer / Camera Preview:
- 通過直接讀取圖像源生成圖像數(shù)據(jù)。
-
NDK (Skia) / OpenGL ES:
- 通過自身的繪制能力生產(chǎn)圖像數(shù)據(jù)。
3.3 Skia 與 OpenGL 的區(qū)別
-
OpenGL:
- 是一種跨平臺(tái)的 3D 圖形繪制規(guī)范接口;
- OpenGL ES 是其針對(duì)嵌入式設(shè)備(如手機(jī))的優(yōu)化版本。
-
Skia:
- 是一個(gè)2D 圖形渲染庫,可獨(dú)立完成 2D 繪制;
- 支持 CPU 軟件繪制 和 GPU 硬件加速;
- 3D 效果(依賴硬件)由 OpenGL、Vulkan、Metal 支持;
- Android、Flutter 均使用 Skia 完成繪制。
-
Vulkan:
- Android 引入 Vulkan 支持;
- 用于替換 OpenGL;
- 不僅支持 3D,也支持 2D;
- 更加輕量級(jí)。
3.4 硬件加速機(jī)制
- 柵格化(Rasterization)是繪制前的關(guān)鍵步驟,但非常耗時(shí);
-
硬件加速思想:
- 將 CPU 不擅長(zhǎng)的圖形計(jì)算轉(zhuǎn)換為 GPU 專用指令;
- 由 GPU 完成繪制任務(wù)。
-
本質(zhì):
- 使用 GPU 代替 CPU 完成 Graphic Buffer 繪制工作,以提升性能。
硬件加速演進(jìn):
- Android 4.0 開始默認(rèn)開啟硬件加速;
- 但部分 API 不支持硬件加速,需手動(dòng)關(guān)閉;
-
Skia 與硬件加速:
- 軟件繪制使用 Skia 庫;
- 不代表 Skia 不支持硬件加速;
- Android 8 開始可選擇使用 Skia 進(jìn)行硬件加速;
- Android 9 開始默認(rèn)使用 Skia 進(jìn)行硬件加速;
- Skia 的硬件加速主要通過
copybit模塊調(diào)用 OpenGL 或 Skia 自身實(shí)現(xiàn)。
3.5 渲染緩沖區(qū)與“黃油計(jì)劃”
- 圖像生產(chǎn)者(OpenGL、Skia、Vulkan)將繪制數(shù)據(jù)存入圖像緩沖區(qū);
-
SurfaceFlinger從緩沖區(qū)取出數(shù)據(jù),進(jìn)行加工與合成。

-
VSync 信號(hào)驅(qū)動(dòng):
- 系統(tǒng)在收到 VSync 信號(hào)后(每 16ms 一次),立即開始下一幀渲染;
- CPU 和 GPU 在收到信號(hào)后立刻開始計(jì)算,并將數(shù)據(jù)寫入 buffer。
Android 5.0 的兩大改進(jìn):
-
引入 RenderNode:
- 對(duì)
DisplayList及 View 顯示屬性進(jìn)一步封裝。
- 對(duì)
-
引入 RenderThread:
- 所有 GL 命令執(zhí)行都放到此線程;
-
RenderNode中存有渲染幀的全部信息; - 可執(zhí)行屬性動(dòng)畫,即使主線程有耗時(shí)操作,也能保證動(dòng)畫流暢。
3.6 SurfaceFlinger —— 圖像消費(fèi)者核心
- SurfaceFlinger 是 Android 系統(tǒng)中最重要的圖像消費(fèi)者;
- 所有 Activity 繪制的界面圖像都會(huì)傳遞給它;
- 主要作用:
- 接收
GraphicBuffer; - 交給
HWComposer或OpenGL做合成; - 合成完成后,將最終數(shù)據(jù)提交給
FrameBuffer。
- 接收
3.7 渲染主流程總結(jié)
-
onMeasure、onLayout計(jì)算出 View 的大小和位置(UI 線程); -
draw方法中進(jìn)行繪制,但并未真正繪制; - 繪制指令被封裝為
DisplayList,進(jìn)一步封裝為RenderNode; -
RenderNode同步給RenderThread; -
RenderThread通過dequeue從SurfaceFlinger的BufferQueue獲取GraphicBuffer; - 根據(jù)繪制指令調(diào)用 OpenGL 接口,通過 GPU 渲染到離屏緩沖區(qū);
- 渲染完成后,將緩沖區(qū)交還給
SurfaceFlinger的BufferQueue; -
SurfaceFlinger通過硬件設(shè)備進(jìn)行 layer 合成,最終展示到屏幕。
四、View 動(dòng)畫機(jī)制
4.1 View 動(dòng)畫 vs 屬性動(dòng)畫
-
View 動(dòng)畫(Animation)不會(huì)修改 View 的屬性值;
- 例如:平移動(dòng)畫后,View 的實(shí)際位置仍在原處;
- 僅視覺上移動(dòng)。
4.2 動(dòng)畫執(zhí)行流程
- 初始化動(dòng)畫基本信息;
-
不立即執(zhí)行,而是走到
ViewRootImpl中注冊(cè) VSYNC 信號(hào); - 等待下一次 VSYNC 到來,執(zhí)行
scheduleTraversals; - 在
scheduleTraversals中執(zhí)行 View 的三大流程;- 但
measure和layout不會(huì)執(zhí)行(因不需要);
- 但
- 在
draw過程中順便執(zhí)行動(dòng)畫;- 一次只執(zhí)行一幀;
- 若動(dòng)畫未完成,則調(diào)用
invalidate()觸發(fā)下一幀。
4.3 性能優(yōu)化
- 只有參與動(dòng)畫的 View 才走 draw 流程;
-
Animation內(nèi)部通過重繪申請(qǐng),由ViewRootImpl監(jiān)聽 VSYNC 信號(hào); - 在
performDraw中遍歷 View 樹,遇到需執(zhí)行動(dòng)畫的 View 則執(zhí)行該幀動(dòng)畫。
五、從生命周期分析 UI 原理
-
attach 方法:
- 創(chuàng)建
PhoneWindow。
- 創(chuàng)建
-
onCreate 方法:
-
setContentView調(diào)用PhoneWindow的setContentView; - 創(chuàng)建
DecorView; - 將 XML 布局解析后添加到
DecorView中。
-
-
onResume 方法執(zhí)行后:
- 創(chuàng)建
ViewRootImpl(最頂級(jí)的 View,DecorView的 parent); - 調(diào)用
setView方法。
- 創(chuàng)建
-
ViewRootImpl.setView 方法:
- 將
PhoneWindow添加到 WMS 中(通過Session作為媒介); - 調(diào)用
requestLayout,發(fā)起繪制請(qǐng)求。
- 將
-
requestLayout 流程:
- 最終調(diào)用
performTraversals; - 執(zhí)行 View 的
measure、layout、draw三大流程; -
draw方法需要傳入Canvas參數(shù)。
- 最終調(diào)用
-
Surface 綁定與提交:
- 通過
relayoutWindow方法將Surface與當(dāng)前 Window 綁定; - 通過
Surface.lockCanvas()獲取 Canvas; - View 的繪制通過此 Canvas 完成;
- 最后通過
Surface.unlockCanvasAndPost()提交繪制數(shù)據(jù); - 數(shù)據(jù)最終交給
SurfaceFlinger提交至屏幕顯示。
- 通過
以下四張圖清晰地展示了 View 在 Activity 不同生命周期階段的狀態(tài)變化:




六、UI 優(yōu)化要點(diǎn)
6.1 卡頓的根本原因
UI 卡頓主要發(fā)生在滑動(dòng)或首次打開頁面時(shí),原因包括:
-
inflate 布局耗時(shí):
- 讀取、解析 layout XML 文件耗時(shí);
- 反射創(chuàng)建 View 耗時(shí)。
-
過度嵌套:
- 導(dǎo)致 measure、layout、render(過度繪制)耗時(shí)增加。
-
onDraw 中頻繁觸發(fā) GC:
- 如大量拼接 String 等操作。
主線程執(zhí)行耗時(shí)任務(wù)。
-
主線程同步鎖競(jìng)爭(zhēng):
- 與其他線程競(jìng)爭(zhēng)鎖資源,導(dǎo)致阻塞。
-
系統(tǒng)負(fù)載過高:
- 同一時(shí)間線程過多,造成 CPU/IO 負(fù)載過高。
6.2 優(yōu)化策略
-
針對(duì)性優(yōu)化底層流程:
- 優(yōu)化 layout XML 讀??;
- 優(yōu)化 View 創(chuàng)建(如使用 ViewStub、Merge、ConstraintLayout 減少嵌套)。
-
場(chǎng)景化排查:
- 對(duì)歸類的卡頓場(chǎng)景逐一分析、優(yōu)化。
【重要提醒】
開發(fā)者常在高端機(jī)測(cè)試,對(duì)卡頓感知不明顯;
但線上用戶多處于弱網(wǎng)/低端機(jī)/多任務(wù)并行環(huán)境,卡頓感知強(qiáng)烈。
6.3 開發(fā)者現(xiàn)狀統(tǒng)計(jì)(官方數(shù)據(jù))
- 超過一半開發(fā)者未寫過自定義 ViewGroup;
- 接近六成開發(fā)者不知道 MeasureSpec 的算法;
- 80% 的開發(fā)者不會(huì)優(yōu)化 UI 性能。
6.4 UI 本質(zhì)原理
Android 渲染機(jī)制的核心是一個(gè) View 從:
- 文本數(shù)據(jù)(layout XML)
- → 實(shí)例數(shù)據(jù)(View 對(duì)象)
- → 圖像數(shù)據(jù)(由 OpenGL 調(diào)用 GPU 生成的 bitmap)
- → 推送至屏幕顯示的過程。
中間還涉及 SurfaceFlinger 進(jìn)程對(duì)驅(qū)動(dòng)的處理。
七、參考資料(原樣保留)
- View的工作原理
- 通俗易懂,Android視圖系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
- Android 渲染
- View的生命周期
- 從XML到View顯示在屏幕
- 完全了解setContentView()
- SurfaceView 設(shè)計(jì)和實(shí)現(xiàn)
- Android View動(dòng)畫主流程全解析:
- UI本質(zhì)原理和優(yōu)化
- Windows的創(chuàng)建過程