Android 繪制知識(shí)點(diǎn)總結(jié)
Android繪制流程
-
繪制前世今生
我們都知道,當(dāng)一個(gè)
Activity
調(diào)用setContenView
之后,我們就可以看到我們的內(nèi)容被展示出來(lái),那么到底是誰(shuí),將我們的頁(yè)面內(nèi)容,轉(zhuǎn)變成屏幕顯示的像素?cái)?shù)據(jù)呢,我們從來(lái)一步步分析
DecorView
是我們應(yīng)用窗口的一個(gè)根容器,SetContentView
,其實(shí)就是將內(nèi)容防止到DecorView
的子元素id
為ContentView
的容器中-
Window
窗口,每一個(gè)窗口有一個(gè)和它關(guān)聯(lián)的Surface
,窗口的Surface
由WMS
分配,畫(huà)在表面上的內(nèi)容并不會(huì)馬上顯示在屏幕上骂束,繪制完成之后,SurfaceFlinger
將多塊Surface
按照順序(Z-order
)進(jìn)行混合,輸出到FrameBuff
,顯示到界面SurfaceView
就是有一個(gè)單獨(dú)Surface
-
Window
具體實(shí)現(xiàn)為PhoneWindow
,和Activity
唯一對(duì)應(yīng),PhoneWindow
中持有DecorView
,Activity.setContentView
的操作就是通過(guò)PhoneWindow來(lái)完成
Activity
,Window
,View
的三者關(guān)系,Activity
工匠,Window
窗戶,View
窗花 ViewRoot
,實(shí)現(xiàn)類ViewRootImple
,會(huì)在ActivityThread.handleResumeActivity()
和DecorView
進(jìn)行關(guān)聯(lián),而后會(huì)調(diào)用requestLayout
發(fā)起繪制-
繪制的起點(diǎn),
ViewRootImpl.requestLayout ->scheduleTraversals() -> performTraversals()
-
measure
-> 計(jì)算出每個(gè)控件需要占據(jù)的尺寸-
MeasureSpec
,32
整數(shù),SpecMode
高2
位+SpecSize
剩余30
位,- 測(cè)量模式
-
EXACTLY
精準(zhǔn)模式,指定了具體的尺寸,如12dp
,或者是match_parent
-
AT_MOST
,最大模式,不得超過(guò)SpecSize
-
UNSPECIFIED
,未定義,不做限制,類似ListView
這種系統(tǒng)內(nèi)部控件
-
- 測(cè)量模式
-
-
layout
->計(jì)算出每個(gè)控件的繪制位置 -
draw
-> 每個(gè)空間的具體繪制內(nèi)容
-
-
繪制核心
SurfaceFlinger
無(wú)論是
OpenGL ES
還是Canvas
,獲取MediaPlayer
等生產(chǎn)者產(chǎn)生的圖像數(shù)據(jù)都會(huì)輸出到Surface
,Surface
也是作為SurfaceFlinger
的生產(chǎn)者,將GraphicBuffer
通過(guò)IPC
傳到SurfaceFlinger
,-
最終
SurfaceFlinger
根據(jù)WMS
提供的窗口信息,合成所有的Layer
(
Layer
和Surface相關(guān)聯(lián),Surface
是Layer
的屏幕內(nèi)存的直接表現(xiàn))
- 圖形繪制,不同的方式,都是輸出到
Surface
-
Canvas
-
Sika
軟件繪制 -
hwui
硬件加速繪制
-
OpenGL ES
-
-
BufferQueue
,在之前的生產(chǎn)者-消費(fèi)者模式中,所有的數(shù)據(jù)傳輸都是通過(guò)BufferQueue
-
Canvas/OpenGLES
請(qǐng)求一塊空閑的緩存區(qū)填充圖像數(shù)據(jù)dequeueBuffer()
,然后將數(shù)據(jù)返回給隊(duì)列:queueBuffer()
-
SurfaceFlinger
合成所有圖層數(shù)據(jù),放到輸出緩存區(qū) -
Display
顯示控制器 獲取合成結(jié)果acquireBuffer()
,使用完畢,則返回給隊(duì)列:releaseBuffer()
-
-
雙緩沖
CPU
計(jì)算好顯示內(nèi)容之后,GPU
負(fù)責(zé)渲染到FrameBuffer
幀緩沖,然后按照VSync
信用從幀緩沖中取出數(shù)據(jù)顯示到屏幕上,如果只有一塊緩沖區(qū),那么數(shù)據(jù)的更新可能就是表現(xiàn)為逐行的刷新,為了刷新是整幀的,需要雙緩沖,刷新時(shí)進(jìn)行直接替換
Android硬件加速
在
Android
中,硬件加速,就是將View
的繪制工作交給GPU
來(lái)完成,Android4.0
以后,硬件加速都是默認(rèn)開(kāi)啟的
Android
硬件加速踩坑
-
一些
Android 4.0
的低端機(jī)型,出現(xiàn)花屏,黑色像素,錯(cuò)位等現(xiàn)象,錯(cuò)誤日志OpenGLRender:0x510
-
針對(duì)一些自定義的復(fù)雜的
View
,可能會(huì)存在渲染異常,出現(xiàn)這種情況,可以優(yōu)先考慮關(guān)閉硬件加速-
Application
級(jí)別關(guān)閉,慎用,這可能會(huì)讓你整個(gè)應(yīng)用的渲染性能下降<application android:hardwareAccelerated="false">
-
Activity
級(jí)別關(guān)閉,悠著點(diǎn),影響也不小<activity android:hardwareAccelerated="false">
-
Window
級(jí)別,悠著點(diǎn),也是和Activity
類似getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
-
View
級(jí)別,這個(gè)比較推薦!!view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);//指定為軟件渲染 //也可以在布局文件xml中 android:layerType="software"
-
-
-
針對(duì)一些繪制相關(guān)的操作,也有一些
api
是在特定版本后才開(kāi)始支持硬件加速的
-
Api
支持版本 canvas.clipRegion()
18 canvas.clipPath()
18
-