SurfaceView源碼分析

基于 android-27源碼
https://blog.csdn.net/Luoshengyang/article/details/8661317

1. 什么是SurfaceView,和普通的View的區(qū)別?

  1. View適用于主動(dòng)更新的情況,而SurfaceView則適用于被動(dòng)更新的情況,比如頻繁刷新界面。
  2. View在主線程中對(duì)頁面進(jìn)行刷新,而SurfaceView則開啟一個(gè)子線程來對(duì)頁面進(jìn)行刷新。
  3. View在繪圖時(shí)沒有實(shí)現(xiàn)雙緩沖機(jī)制,SurfaceView在底層機(jī)制中就實(shí)現(xiàn)了雙緩沖機(jī)制。(當(dāng)一個(gè)動(dòng)畫爭先顯示時(shí),程序又在改變它,前面還沒有顯示完,程序又請(qǐng)求重新繪制,這樣屏幕就會(huì)不停地閃爍。而雙緩沖技術(shù)是把要處理的圖片在內(nèi)存中處理好之后,再將其顯示在屏幕上。雙緩沖主要是為了解決 反復(fù)局部刷屏帶來的閃爍。把要畫的東西先畫到一個(gè)內(nèi)存區(qū)域里,然后整體的一次性畫出來。)

2. SurfaceView的繪制原理

Android應(yīng)用程序窗口是通過SurfaceFlinger服務(wù)來繪制自己的UI。一般來說,每一個(gè)窗口在SurfaceFlinger服務(wù)中都對(duì)應(yīng)有一個(gè)Layer,用來描述它的繪圖表面。對(duì)于那些具有SurfaceView的窗口來說,每一個(gè)SurfaceView在SurfaceFlinger服務(wù)中還對(duì)應(yīng)有一個(gè)獨(dú)立的Layer或者LayerBuffer,用來單獨(dú)描述它的繪圖表面,以區(qū)別于它的宿主窗口的繪圖表面。


image

3. SurfaceView的繪制過程

image

SurfaceView的繪圖表面的創(chuàng)建過程從ViewRoot類的成員函數(shù)performTraversals開始

3.1 ViewRoot.performTraversals

host = ViewRoot.mView 指向DecorView對(duì)象,描述當(dāng)前窗口的頂級(jí)視圖
attachInfo = ViewRoot.mAttachInfo 指向的AttachInfo是描述串口信息對(duì)象
這個(gè)方法的主要作用是判斷繪圖表面是否創(chuàng)建,通知View(ViewGroup)附加到窗口 3.2 ,判斷并通知當(dāng)前窗口的可見性是否變化 3.5

3.2 ViewGroup.dispatchAttachedToWindow() (DecorView ViewGroup)

遍歷通知子視圖添加到窗口 3.3

3.3 View.dispatchAttachedToWindow() (View)

保存窗口信息mAttachInfo,調(diào)用子類的onAttachedToWindow 3.4

3.4 SurfaceView.onAttachedToWindow() (SurfaceView)

主要做了兩件事:
1 通知父視圖,當(dāng)前正在處理的SurfaceView需要在宿主窗口的繪圖表面上挖一個(gè)洞,即需要在宿主窗口的繪圖表面上設(shè)置一塊透明區(qū)域。 待更新
2 調(diào)用從父類View繼承下來的成員函數(shù)getWindowSession()來獲得一個(gè)實(shí)現(xiàn)了IWindowSession接口的Binder代理對(duì)象. mSession指向這個(gè)對(duì)象.主要是想通過binder請(qǐng)求繪制繪圖表面.

3.5 ViewGroup.dispatchWindowVisibilityChanged() (DecorView ViewGroup)

遍歷設(shè)置子View的可見性 3.6

3.6 View.dispatchWindowVisibilityChanged() (View)

調(diào)用成員函數(shù)onWindowVisibilityChanged()來讓子類處理可見性變化

3.7 SurfaceView.onWindowVisibilityChanged() (SurfaceView)

設(shè)置當(dāng)前SurfaceView的可見性,調(diào)用3.8方法,更新視圖,如果還沒有繪制SurfaceView,就請(qǐng)求繪制

3.8 SurfaceView.updateWindow

重要的成員變量解釋:
mSurface:指向特定的繪圖表面,其他的View的繪圖表面是共享的,SurfaceView的是特有的;
mWindow:指向MyWindow對(duì)象,每個(gè)SurfaceView都關(guān)聯(lián)了一個(gè)實(shí)現(xiàn)了IWindow接口的Binder本地對(duì)象
mWindowType:描述SurfaceView的窗口類型,默認(rèn)是顯示多媒體的類型,可通過窗口設(shè)置層級(jí),比如media的在下面,media_overlate的在上面;也可以通過修改setZXXX()的值來提升在Z軸的顯示層級(jí)
mRequestedType:繪圖表面類型 Layer or LayerBuffer 對(duì)應(yīng)的在SurfaceFlinger的內(nèi)存分配也不一致.

3.8.1 繪圖表面的創(chuàng)建過程:
  1. 判斷并準(zhǔn)備宿主窗口

  2. 獲得SurfaceView寬高

  3. 更新記錄SurfaceView的繪制信息,可見性、位置、大小、繪圖表面像素格式和類型等等
    image
  4. 檢查成員變量mWindow的值是否等于null,相當(dāng)于檢測是否添加到WindowManagerService服務(wù)

  5. 調(diào)用成員變量mSession所描述的一個(gè)Binder代理對(duì)象的成員函數(shù)relayout來請(qǐng)求WindowManagerService服務(wù)對(duì)SurfaceView的UI進(jìn)行布局

3.8.2 SurfaceView的挖洞過程:
image
  1. SurfaceView -- SurfaceView.onAttachedToWindow
    調(diào)用mParent.requestTransparentRegion(SurfaceView.this);來請(qǐng)求在宿主窗口挖洞;
  2. ViewGroup -- requestTransparentRegion()
    設(shè)置標(biāo)志位mPrivateFlags為頂層繪制透明窗口,調(diào)用mParent(ViewRoot)的方法;
  3. ViewRootImp -- requestTransparentRegion()
    檢查線程(為非主線程),檢查viewRoot指向的對(duì)象和傳入的參數(shù)是否是同個(gè)對(duì)象;設(shè)置標(biāo)志位,調(diào)requestLayout()開始刷新窗口;
  4. ViewRootImp -- performTraversals()
    在窗口的UI布局完成之后,并且在窗口的UI繪制之前,收集嵌入在它里面的SurfaceView所設(shè)置的透明區(qū)域的,這樣子View的大小和位置才能確定;
  5. ViewGroup -- gatherTransparentRegion() 挖洞過程
    5.1 調(diào)用父類View的成員函數(shù)gatherTransparentRegion來檢查當(dāng)前正在處理的視圖容器是否需要繪制。
    5.2 遍歷子類的gatherTransparentRegion來繼續(xù)往下收集透明區(qū)域。
  6. SurfaceView -- gatherTransparentRegion()
    假設(shè)當(dāng)前正在處理的SurfaceView不是用作窗口面板,并且也是不需要在宿主窗口的繪圖表面上進(jìn)行繪制的,而參數(shù)region的值又不等于null,那么SurfaceView類的成員函數(shù)gatherTransparentRegion就會(huì)先計(jì)算好當(dāng)前正在處理的SurfaceView所占據(jù)的區(qū)域,然后再將該區(qū)域添加到參數(shù)region所描述的區(qū)域中去,這樣就可以得到窗口的一個(gè)新的透明區(qū)域。
3.8.3 SurfaceView的繪制過程:
image.png

如果要在一個(gè)繪圖表面進(jìn)行UI繪制,那么就順序執(zhí)行以下的操作:
(1). 在繪圖表面的基礎(chǔ)上建立一塊畫布,即獲得一個(gè)Canvas對(duì)象。
(2). 利用Canvas類提供的繪圖接口在前面獲得的畫布上繪制任意的UI。
(3). 將已經(jīng)填充好了UI數(shù)據(jù)的畫布緩沖區(qū)提交給SurfaceFlinger服務(wù),以便SurfaceFlinger服務(wù)可以將它合成到屏幕上去。
SurfaceView提供了一個(gè)SurfaceHolder接口,通過這個(gè)SurfaceHolder接口就可以執(zhí)行第(1)和引(3)個(gè)操作;

SurfaceView sv = (SurfaceView )findViewById(R.id.surface_view);
SurfaceHolder sh = sv.getHolder();
Cavas canvas = sh.lockCanvas()
//Draw something on canvas
......
sh.unlockCanvasAndPost(canvas);
  1. SurfaceView.getHolder
    獲得SurfaceHolder對(duì)象
  2. SurfaceHolder.lockCanvas
    因?yàn)镾urfaceView是在子線程執(zhí)行繪制的,畫布的繪制過程不是線程安全的,所以在繪制的時(shí)候需要對(duì)當(dāng)前的繪圖表面進(jìn)行鎖保護(hù)--mSurfaceLock;
  3. Surface.lockCanvas
    通過JNI方法來在當(dāng)前正在處理的繪圖表面上獲得一個(gè)圖形緩沖區(qū),并且將這個(gè)圖形繪沖區(qū)封裝在一塊類型為Canvas的畫布中返回給調(diào)用者使用。
mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);

這樣就可以開始在mCanvas中繪制

  1. SurfaceHolder.unlockCanvasAndPost
    SurfaceHolder類的成員函數(shù)unlockCanvasAndPost再調(diào)用當(dāng)前正在處理的SurfaceView的成員變量mSurfaceLock所指向的一個(gè)ReentrantLock對(duì)象的成員函數(shù)unlock來解鎖當(dāng)前正在處理的SurfaceView的繪圖表面
  2. Surface.unlockCanvasAndPost
mHwuiContext.unlockAndPost(canvas);

將在前面的Step 3中所獲得的一個(gè)圖形緩沖區(qū)提交給SurfaceFlinger服務(wù),以便SurfaceFlinger服務(wù)可以在合適的時(shí)候?qū)⒃搱D形緩沖區(qū)合成到屏幕上去顯示,這樣就可以將對(duì)應(yīng)的SurfaceView的UI展現(xiàn)出來了;繪制后釋放鎖

總結(jié):

SurfaceView有以下三個(gè)特點(diǎn):
A. 具有獨(dú)立的繪圖表面;
B. 需要在宿主窗口上挖一個(gè)洞來顯示自己;
C. 它的UI繪制可以在獨(dú)立的線程中進(jìn)行,這樣就可以進(jìn)行復(fù)雜的UI繪制,并且不會(huì)影響應(yīng)用程序的主線程響應(yīng)用戶輸入。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容