原文:http://blog.csdn.net/luoshengyang/article/details/8303098。原文代碼比較老了,但是核心不變。在原文基礎上改動了一些代碼,以及增長本身少許的理解。java
在上一篇博客中。咱們分析了應用程序窗體鏈接到WindowManagerService服務的過程。android
在這個過程當中,WindowManagerService服務會爲應用程序窗體建立過一個到SurfaceFlinger服務的鏈接。c++
有了這個鏈接以後。WindowManagerService服務就可以爲應用程序窗體建立畫圖表面了,以便可以用來渲染窗體的UI。安全
在本文中,咱們就具體分析應用程序窗體的畫圖表面的建立過程。session
這節主要是把http://blog.csdn.net/luoshengyang/article/details/8303098這篇博客複製過來的app
每一個在C++層實現的應用程序窗體都需要有一個畫圖表面,而後才幹夠將本身的UI表現出來。這個畫圖表面是需要由應用程序進程請求SurfaceFlinger服務來建立的,在SurfaceFlinger服務內部使用一個Layer對象來描寫敘述,同一時候,SurfaceFlinger服務會返回一個實現了ISurface接口的Binder本地對象給應用程序進程,因而,應用程序進程就可以得到一個實現了ISurface接口的Binder代理對象。有了這個實現了ISurface接口的Binder代理對象以後。在C++層實現的應用程序窗體就可以請求SurfaceFlinger服務分配圖形緩衝區以及渲染已經填充好UI數據的圖形緩衝區了。框架
對於在Java層實現的Android應用程序窗體來講,它也需要請求SurfaceFlinger服務爲它建立畫圖表面,這個畫圖表面使用一個Surface對象來描寫敘述。由於在Java層實現的Android應用程序窗體還要接受WindowManagerService服務管理。所以,它的畫圖表面的建立流程就會比在C++層實現的應用程序窗體複雜一些。ide
詳細來講,就是在在Java層實現的Android應用程序窗體的畫圖表面是經過兩個Surface對象來描寫敘述,一個是在應用程序進程這一側建立的,還有一個是在WindowManagerService服務這一側建立的,它們相應於SurfaceFlinger服務這一側的同一個Layer對象,如圖所看到的:函數
在應用程序進程這一側。每一個應用程序窗體,即每一個Activity組件,都有一個關聯的Surface對象,這個Surface對象是保在在一個關聯的ViewRootImpl對象的成員變量mSurface中的。oop
這裏咱們僅僅關注Surface類的實現。在應用程序進程這一側,每一個Java層的Surface對都相應有一個C++層的Surface對象。
在WindowManagerService服務這一側。每一個應用程序窗體,即每一個Activity組件。都有一個相應的WindowState對象,這個WindowState對象的成員變量mSurface相同是指向了一個Surface對象,在WindowManagerService服務這一側,每一個Java層的Surface對都相應有一個C++層的SurfaceControl對象。
一個應用程序窗體分別位於應用程序進程和WindowManagerService服務中的兩個Surface對象有什麼差異呢?儘管它們都是用來操做位於SurfaceFlinger服務中的同一個Layer對象的,只是,它們的操做方式卻不同。詳細來講,就是位於應用程序進程這一側的Surface對象負責繪製應用程序窗體的UI,即往應用程序窗體的圖形緩衝區填充UI數據,而位於WindowManagerService服務這一側的Surface對象負責設置應用程序窗體的屬性。好比位置、大小等屬性。這兩種不一樣的操做方式各自是經過C++層的Surface對象和SurfaceControl對象來完畢的。所以。位於應用程序進程和WindowManagerService服務中的兩個Surface對象的使用方法是有差異的。
之因此會有這種差異。是因爲繪製應用程序窗體是獨立的,由應用程序進程來完就能夠。而設置應用程序窗體的屬性卻需要全局考慮,即需要由WindowManagerService服務來統籌安排,好比,一個應用程序窗體的Z軸座標大小要考慮它到的窗體類型以及它與系統中的其餘窗體的關係。
講到這裏。另一個問題又來了,由於一個應用程序窗體相應有兩個Surface對象,那麼它們是怎樣建立出來的呢?簡單地說,就是依照下面步驟來建立:
1. 應用程序進程請求WindowManagerService服務爲一個應用程序窗體建立一個Surface對象;
2. WindowManagerService服務請求SurfaceFlinger服務建立一個Layer對象,並且得到一個ISurface接口。
3. WindowManagerService服務將得到的ISurface接口保存在其內部的一個Surface對象中,並且將該ISurface接口返回給應用程序進程;
4. 應用程序進程獲得WindowManagerService服務返回的ISurface接口以後,再將其封裝成其內部的另一個Surface對象中。
那麼應用程序窗體的畫圖表面又是何時建立的呢?一般是在不存在的時候就建立。因爲應用程序窗體在執行的過程當中,它的畫圖表面會依據需要來銷燬以及又一次建立的,好比。應用程序窗體在第一次顯示的時候,就會請求WindowManagerService服務爲其建立繪製表面。從前面Android應用程序窗體(Activity)的視圖對象(View)的建立過程分析一文可以知道,當一個應用程序窗體被激活並且它的視圖對象建立完畢以後,應用程序進程就會調用與其所關聯的一個ViewRootImpl對象的成員函數requestLayout來請求對其UI進行佈局以及顯示。因爲這時候應用程序窗體的畫圖表面還沒有建立,所以,ViewRoot類的成員函數requestLayout就會請求WindowManagerService服務來建立畫圖表面。接下來,咱們就從ViewRoot類的成員函數requestLayout開始。分析應用程序窗體的畫圖表面的建立過程。如圖所看到的:
以前咱們分析過在ViewRootImpl的setView函數中調用了requestLayout函數,現在咱們來分析下這個函數。 ViewRootImpl類的成員函數requestLayout首先調用另一個成員函數checkThread來檢查當前線程是否就是建立當前正在處理的ViewRootImpl對象的線程。假設不是的話,那麼ViewRoot類的成員函數checkThread就會拋出一個異常出來。ViewRoot類是從Handler類繼承下來的,用來處理應用程序窗體的UI佈局和渲染等消息。
由於這些消息都是與Ui相關的。所以它們就需要在UI線程中處理,這樣咱們就可以判斷出當前正在處理的ViewRootImpl對象是要應用程序進程的UI線程中建立的。進一步地,咱們就可以判斷出ViewRootImpl類的成員函數checkThread實際上就是用來檢查當前線程是不是應用程序進程的UI線程,假設不是的話。它就會拋出一個異常出來。
經過了上述檢查以後,ViewRootImpl類的成員函數requestLayout首先將其成員變量mLayoutRequested的值設置爲true。表示應用程序進程的UI線程正在被請求運行一個UI佈局操做。接着再調用另一個成員函數scheduleTraversals來繼續運行UI佈局的操做。
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
在scheduleTraversals函數中,ViewRootImpl類的成員變量mTraversalScheduled用來表示應用程序進程的UI線程是否已經在scheduleTraversals。假設已經調度了的話。它的值就會等於true。在這樣的狀況下, ViewRootImpl類的成員函數scheduleTraversals就什麼也不作。不然的話,它就會首先將成員變量mTraversalScheduled的值設置爲true,就會發送一個消息來處理mTraversalRunnable這個Runnable。
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }咱們來看下TraversalRunnable 這個Runnable,就是調用了doTraversal函數
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
而在doTraversal這個函數中調用了performTraversals函數
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
ViewRootImpl類的成員函數performTraversals的實現是至關複雜的。這裏咱們分析它的實現框架.
在分析ViewRootImpl類的成員函數performTraversals的實現框架以前,咱們首先了解ViewRoot類的下面五個成員變量:
--mView:它的類型爲View,但它實際上指向的是一個DecorView對象。用來描寫敘述應用程序窗體的頂級視圖,這一點可以參考前面Android應用程序窗體(Activity)的視圖對象(View)的建立過程分析一文。
--mLayoutRequested:這是一個布爾變量。用來描寫敘述應用程序進程的UI線程是否需要正在被請求運行一個UI佈局操做。
--mFirst:這是一個布爾變量,用來描寫敘述應用程序進程的UI線程是否第一次處理一個應用程序窗體的UI。
--mFullRedrawNeeded:這是一個布爾變量。用來描寫敘述應用程序進程的UI線程是否需要將一個應用程序窗體的全部區域都又一次繪製。
--mSurface:它指向一個Java層的Surface對象。用來描寫敘述一個應用程序窗體的畫圖表面。
注意,成員變量mSurface所指向的Surface對象在建立的時候,尚未在C++層有一個關聯的Surface對象,所以。這時候它描寫敘述的就是一個無效的畫圖表面。另外,這個Surface對象在應用程序窗體執行的過程當中,也會可能被銷燬,所以,這時候它描寫敘述的畫圖表面也會變得無效。在上述兩種狀況中,咱們都需要請求WindowManagerService服務來爲當前正在處理的應用程序窗體建立有一個有效的畫圖表面,以便可以在上面渲染UI。這個建立畫圖表面的過程正是本文所要關心的。
理解了上述五個成員變量以後。咱們就可以分析ViewRootImpl類的成員函數performTraversals的實現框架了,例如如下所看到的:
1. 將成員變量mView和mFullRedrawNeeded的值分別保存在本地變量host和fullRedrawNeeded中。並且將成員變量mTraversalScheduled的值設置爲false,表示應用程序進程的UI線程中的消息已經被處理了。
2. 本地變量newSurface用來描寫敘述當前正在處理的應用程序窗體在本輪的消息處理中是否新建立了一個畫圖表面。它的初始值爲false。
3. 假設成員變量mLayoutRequested的值等於true,那麼就表示應用程序進程的UI線程正在被請求對當前正在處理的應用程序窗體運行一個UI佈局操做,所以,這時候就會調用本地變量host所描寫敘述的一個頂層視圖對象的成員函數measure來測量位於各個層次的UI控件的大小。
4. 假設當前正在處理的應用程序窗體的UI是第一次被處理。即成員變量mFirst的值等於true,或者當前正在處理的應用程序窗體的大小發生了變化。即本地變量windowShouldResize的值等於true,或者當前正在處理的應用程序窗體的邊襯發生了變化。即本地變量insetsChanged的值等於true,或者正在處理的應用程序窗體的可見性發生了變化。即本地變量viewVisibilityChanged的值等於true。或者正在處理的應用程序窗體的UI佈局參數發生了變化,即本地變量params指向了一個WindowManager.LayoutParams對象,那麼應用程序進程的UI線程就會調用另一個成員函數relayoutWindow來請求WindowManagerService服務又一次佈局系統中的所有窗體。WindowManagerService服務在又一次佈局系統中的所有窗體的過程當中,假設發現當前正在處理的應用程序窗體還沒有具備一個有效的畫圖表面,那麼就會爲它建立一個有效的畫圖表面。這一點是咱們在本文中所要關注的。
5. 應用程序進程的UI線程在調用ViewRootImpl類的成員函數relayoutWindow來請求WindowManagerService服務又一次佈局系統中的所有窗體以前。會調用成員變量mSurface所指向的一個Surface對象的成員函數isValid來推斷它描寫敘述的是不是一個有效的畫圖表面。並且將結果保存在本地變量hadSurface中。
6. 應用程序進程的UI線程在調用ViewRootImpl類的成員函數relayoutWindow來請求WindowManagerService服務又一次佈局系統中的所有窗體以後,又會繼續調用成員變量mSurface所指向的一個Surface對象的成員函數isValid來推斷它描寫敘述的是不是一個有效的畫圖表面。假設這時候成員變量mSurface所指向的一個Surface對象描寫敘述的是不是一個有效的畫圖表面,並且本地變量hadSurface的值等於false,那麼就說明WindowManagerService服務爲當前正在處理的應用程序窗體新建立了一個有效的畫圖表面。因而就會將本地變量newSurface和fullRedrawNeeded的值均改動爲true。
7. 應用程序進程的UI線程再次推斷mLayoutRequested的值是否等於true。假設等於的話,那麼就說明需要對當前正在處理的應用程序窗體的UI進行又一次佈局,這是經過調用本地變量host所描寫敘述的一個頂層視圖對象的成員函數layout來實現的。在對當前正在處理的應用程序窗體的UI進行又一次佈局以前。應用程序進程的UI線程會將成員變量mLayoutRequested的值設置爲false,表示以前所請求的一個UI佈局操做已經獲得處理了。
8. 應用程序進程的UI線程接下來就要開始對當前正在處理的應用程序窗體的UI進行又一次繪製了,只是在重繪以前,會先詢問一下那些註冊到當前正在處理的應用程序窗體中的Tree Observer,即調用它們的成員函數dispatchOnPreDraw,看看它們是否需要取消接下來的重繪操做,這個詢問結果保存在本地變量cancelDraw中。
9. 假設本地變量cancelDraw的值等於false,並且本地變量newSurface的值也等於false,那麼就說明註冊到當前正在處理的應用程序窗體中的Tree Observer不要求取消當前的此次重繪操做,並且當前正在處理的應用程序窗體也沒有得到一個新的畫圖表面。在這樣的狀況下。應用程序進程的UI線程就會調用ViewRoot類的成員函數draw來對當前正在處理的應用程序窗體的UI進行重繪。
在重繪以前,還會將ViewRoot類的成員變量mFullRedrawNeeded的值重置爲false。
10. 假設本地變量cancelDraw的值等於true,或者本地變量newSurface的值等於true,那麼就說明註冊到當前正在處理的應用程序窗體中的Tree Observer要求取消當前的此次重繪操做。或者當前正在處理的應用程序窗體得到了一個新的畫圖表面。在這兩種狀況下,應用程序進程的UI線程就不能對當前正在處理的應用程序窗體的UI進行重繪了,而是要等到下一個消息到來的時候。再進行重繪。以便使得當前正在處理的應用程序窗體的各項參數可以獲得又一次設置。
下一個消息需要當即被調度。所以。應用程序進程的UI線程就會又一次運行ViewRootImpl類的成員函數scheduleTraversals。
這樣,咱們就分析完畢ViewRoot類的成員函數performTraversals的實現框架了,接下來咱們就繼續分析ViewRootImpl類的成員函數relayoutWindow的實現,以便可以看到當前正在處理的應用程序窗體的畫圖表面是怎樣建立的。
在performTraversals函數中調用了relayoutWindow函數例如如下:
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
而後在relayoutWindow函數中調用了WindowSession的relayout函數
int relayoutResult = mWindowSession.relayout( mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ?WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
咱們先看看IWindowSession函數。這個函數是一個aidl需要在out文件夾下看其java文件
public interface IWindowSession extends android.os.IInterface { ...... public static abstract class Stub extends android.os.Binder implements android.view.IWindowSession { ...... private static class Proxy implements android.view.IWindowSession { private android.os.IBinder mRemote; ...... public int relayout(android.view.IWindow window, android.view.WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, android.graphics.Rect outFrame, android.graphics.Rect outContentInsets, android.graphics.Rect outVisibleInsets, android.content.res.Configuration outConfig, android.view.Surface outSurface) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null))); if ((attrs!=null)) { _data.writeInt(1); attrs.writeToParcel(_data, 0); } else { _data.writeInt(0); } _data.writeInt(requestedWidth); _data.writeInt(requestedHeight); _data.writeInt(viewVisibility); _data.writeInt(((insetsPending)?(1):(0))); mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); if ((0!=_reply.readInt())) { outFrame.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outContentInsets.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outVisibleInsets.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outConfig.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outSurface.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } return _result; } ...... } ...... } ...... }
IWindowSession.Stub.Proxy類的成員函數relayout首先將從前面傳進來的各個參數寫入到Parcel對象_data中,接着再經過其成員變量mRemote所描寫敘述的一個Binder代理對象向執行在WindowManagerService服務內部的一個Session對象發送一個類型爲TRANSACTION_relayout的進程間通訊請求,當中。這個Session對象是用來描寫敘述從當前應用程序進程到WindowManagerService服務的一個鏈接的。
咱們來看下ViewRootImpl的Surface的readFromParcel函數,獲取數據以後調用了setNativeObjectLocked函數
public void readFromParcel(Parcel source) { if (source == null) { throw new IllegalArgumentException("source must not be null"); } synchronized (mLock) { // nativeReadFromParcel() will either return mNativeObject, or // create a new native Surface and return it after reducing // the reference count on mNativeObject. Either way, it is // not necessary to call nativeRelease() here. mName = source.readString(); setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); } }
setNativeObjectLocked函數保存了從WMS傳過來的指針。
private void setNativeObjectLocked(long ptr) { if (mNativeObject != ptr) { if (mNativeObject == 0 && ptr != 0) { mCloseGuard.open("release"); } else if (mNativeObject != 0 && ptr == 0) { mCloseGuard.close(); } mNativeObject = ptr; mGenerationId += 1; if (mHwuiContext != null) { mHwuiContext.updateSurface(); } } }
當執行在WindowManagerService服務內部的Session對象處理完畢當前應用程序進程發送過來的類型爲TRANSACTION_relayout的進程間通訊請求以後,就會將處理結果寫入到Parcel對象_reply中。並且將這個Parcel對象_reply返回給當前應用程序進程處理。返回結果包括了一系列與參數window所描寫敘述的應用程序窗體相關的參數,例如如下所看到的:
1. 窗體的大小:終於保存在輸出參數outFrame中。
2. 窗體的內容區域邊襯大小:終於保存在輸出參數outContentInsets中。
3. 窗體的可見區域邊襯大小:終於保存在輸出參數outVisibleInsets中。
4. 窗體的配置信息:終於保存在輸出參數outConfig中。
5. 窗體的畫圖表面:終於保存在輸出參數outSurface中。
這裏咱們僅僅關注從WindowManagerService服務返回來的窗體畫圖表面是怎樣保存到輸出參數outSurface中的,即關注Surface類的成員函數readFromParcel的實現。從前面的調用過程可以知道,輸出參數outSurface描寫敘述的即是當前正在處理的應用程序窗體的畫圖表面。將WindowManagerService服務返回來的窗體畫圖表面保存在它裏面。就至關因而爲當前正在處理的應用程序窗體建立了一個畫圖表面。
在分析Surface類的成員函數readFromParcel的實現以前,咱們先分析Session類的成員函數relayout的實現,因爲它是用來處理類型爲TRANSACTION_relayout的進程間通訊請求的。
session類的relayout的話最後調用了WMS的relayoutWindow函數
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Configuration outConfig, Surface outSurface) { if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); int res = mService.relayoutWindow(this, window, seq, attrs, requestedWidth, requestedHeight, viewFlags, flags, outFrame, outOverscanInsets, outContentInsets, outVisibleInsets, outStableInsets, outsets, outConfig, outSurface); if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); return res; }
在WMS中後面有例如如下代碼,是把對象傳到outSurface中去了。
SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(); if (surfaceControl != null) { outSurface.copyFrom(surfaceControl);
WindowManagerService類的成員函數relayoutWindow的實現是至關複雜的。這裏咱們僅僅關注與建立應用程序窗體的畫圖表面相關的代碼。
簡單來講。WindowManagerService類的成員函數relayoutWindow依據應用程序進程傳遞過來的一系列數據來又一次設置由參數client所描寫敘述的一個應用程序窗體的大小和可見性等信息。而當一個應用程序窗體的大小或者可見性發生變化以後,系統中當前得到焦點的窗體,以及輸入法窗體和壁紙窗體等均可能會發生變化,而且也會對其餘窗體產生影響,所以,這時候WindowManagerService類的成員函數relayoutWindow就會對系統中的窗體的佈局進行又一次調整。對系統中的窗體的佈局進行又一次調整的過程是整個WindowManagerService服務最爲複雜和核心的內容。咱們相同是在後面的文章中再具體分析。
現在,咱們就主要分析參數client所描寫敘述的一個應用程序窗體的畫圖表面的建立過程。
WindowManagerService類的成員函數relayoutWindow首先得到與參數client所相應的一個WindowState對象win,這是經過調用WindowManagerService類的成員函數windowForClientLocked來實現的。假設這個相應的WindowState對象win不存在,那麼就說明應用程序進程所請求處理的應用程序窗體不存在,這時候WindowManagerService類的成員函數relayoutWindow就直接返回一個0值給應用程序進程。
WindowManagerService類的成員函數relayoutWindow接下來推斷參數client所描寫敘述的一個應用程序窗體是不是可見的。一個窗體僅僅有在可見的狀況下。WindowManagerService服務纔會爲它建立一個畫圖表面。 一個窗體是否可見由下面兩個條件決定:
1. 參數viewVisibility的值等於View.VISIBLE,表示應用程序進程請求將它設置爲可見的。
2. WindowState對象win的成員變量mAppToken不等於null,並且它所描寫敘述的一個AppWindowToken對象的成員變量clientHidden的值等於false。這意味着參數client所描寫敘述的窗體是一個應用程序窗體,即一個Activity組件窗體,並且這個Activity組件當前是處於可見狀態的。當一個Activity組件當前是處於不可見狀態時,它的窗體就也必須是處於不可見狀態。
注意。當WindowState對象win的成員變量mAppToken等於null時。僅僅要知足條件1就可以了。因爲這時候參數client所描寫敘述的窗體不是一個Activity組件窗體。它的可見性不像Activity組件窗體同樣受到Activity組件的可見性影響。
咱們假設參數client所描寫敘述的是一個應用程序窗體,並且這個應用程序窗體是可見的,那麼WindowManagerService類的成員函數relayoutWindow接下來就會調用WindowState對象win的winAnimator的函數createSurfaceLocked來爲它建立一個畫圖表面。假設這個畫圖表面能建立成功,那麼WindowManagerService類的成員函數relayoutWindow就會將它的內容複製到輸出參數outSource所描寫敘述的一個Surface對象去,以便可以將它返回給應用程序進程處理。還有一方面,假設這個畫圖表面不能建立成功。那麼WindowManagerService類的成員函數relayoutWindow就會將輸出參數outSource所描寫敘述的一個Surface對象的內容釋放掉,以便應用程序進程知道該Surface對象所描寫敘述的畫圖表面已經失效了。
接下來,咱們就繼續分析WindowState類的winAnimator的成員函數createSurfaceLocked的實現,以便可以瞭解一個應用程序窗體的畫圖表面的建立過程。
SurfaceControl createSurfaceLocked() { final WindowState w = mWin; if (mSurfaceControl == null) { if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING"); mDrawState = DRAW_PENDING; if (w.mAppToken != null) { if (w.mAppToken.mAppAnimator.animation == null) { w.mAppToken.allDrawn = false; w.mAppToken.deferClearAllDrawn = false; } else { // Currently animating, persist current state of allDrawn until animation // is complete. w.mAppToken.deferClearAllDrawn = true; } } mService.makeWindowFreezingScreenIfNeededLocked(w); int flags = SurfaceControl.HIDDEN; final WindowManager.LayoutParams attrs = w.mAttrs; if (mService.isSecureLocked(w)) { flags |= SurfaceControl.SECURE; } int width; int height; if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) { // for a scaled surface, we always want the requested // size. width = w.mRequestedWidth; height = w.mRequestedHeight; } else { width = w.mCompatFrame.width(); height = w.mCompatFrame.height(); } // Something is wrong and SurfaceFlinger will not like this, // try to revert to sane values if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } float left = w.mFrame.left + w.mXOffset; float top = w.mFrame.top + w.mYOffset; // Adjust for surface insets. width += attrs.surfaceInsets.left + attrs.surfaceInsets.right; height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom; left -= attrs.surfaceInsets.left; top -= attrs.surfaceInsets.top; if (DEBUG_VISIBILITY) { Slog.v(TAG, "Creating surface in session " + mSession.mSurfaceSession + " window " + this + " w=" + width + " h=" + height + " x=" + left + " y=" + top + " format=" + attrs.format + " flags=" + flags); } // We may abort, so initialize to defaults. mSurfaceShown = false; mSurfaceLayer = 0; mSurfaceAlpha = 0; mSurfaceX = 0; mSurfaceY = 0; w.mLastSystemDecorRect.set(0, 0, 0, 0); mHasClipRect = false; mClipRect.set(0, 0, 0, 0); mLastClipRect.set(0, 0, 0, 0); // Set up surface control with initial size. try { mSurfaceW = width; mSurfaceH = height; final boolean isHwAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format; if (!PixelFormat.formatHasAlpha(attrs.format) && attrs.surfaceInsets.left == 0 && attrs.surfaceInsets.top == 0 && attrs.surfaceInsets.right == 0 && attrs.surfaceInsets.bottom == 0) { flags |= SurfaceControl.OPAQUE; } mSurfaceFormat = format; if (DEBUG_SURFACE_TRACE) { mSurfaceControl = new SurfaceTrace(//新建一個SurfaceTrace對象 mSession.mSurfaceSession, attrs.getTitle().toString(), width, height, format, flags); } else { mSurfaceControl = new SurfaceControl( mSession.mSurfaceSession, attrs.getTitle().toString(), width, height, format, flags); }
在建立一個應用程序窗體的畫圖表面以前。咱們需要知道下面數據:
1. 應用程序窗體它所執行的應用程序進程的PID。
2. 與應用程序窗體它所執行的應用程序進程所關聯的一個SurfaceSession對象。
3. 應用程序窗體的標題。
4. 應用程序窗體的像素格式。
5. 應用程序窗體的寬度。
6. 應用程序窗體的高度。
7. 應用程序窗體的圖形緩衝區屬性標誌。
第1個和第2個數據可以經過當前正在處理的WindowState對象的成員變量mSession所描寫敘述的一個Session對象的成員變量mPid和mSurfaceSession來得到。第3個和第4個數據可以經過當前正在處理的WindowState對象的成員變量mAttr所描寫敘述的一個WindowManager.LayoutParams對象的成員函數getTitle以及成員變量format來得到;接下來咱們就分析後面3個數據是怎樣得到的。
WindowState類的成員變量mFrame的類型爲Rect。它用來描寫敘述應用程序窗體的位置和大小,它們是由WindowManagerService服務依據屏幕大小以及其餘屬性計算出來的。所以,經過調用過它的成員函數width和height就可以獲得要建立畫圖表面的應用程序窗體的寬度和高度。並且保存在變量w和h中。
但是,噹噹前正在處理的WindowState對象的成員變量mAttr所描寫敘述的一個WindowManager.LayoutParams對象的成員變量flags的LayoutParams.FLAG_SCALED位不等於0時,就說明應用程序進程指定了該應用程序窗體的大小,這時候指定的應用程序窗體的寬度和高度就保存在WindowState類的成員變量mRequestedWidth和mRequestedHeight中,所以,咱們就需要將當前正在處理的WindowState對象的成員變量mRequestedWidth和mRequestedHeight的值分別保存在變量w和h中。
通過上述兩步計算以後,假設獲得的變量w和h等於0,那麼就說明當前正在處理的WindowState對象所描寫敘述的應用程序窗體的大小尚未通過計算。或者尚未被指定過,這時候就需要將它們的值設置爲1。避免接下來建立一個大小爲0的畫圖表面。
假設當前正在處理的WindowState對象的成員變量mAttr所描寫敘述的一個WindowManager.LayoutParams對象的成員變量memoryType的值等於MEMORY_TYPE_PUSH_BUFFERS,那麼就說明正在處理的應用程序窗體不擁有專屬的圖形緩衝區,這時候就需要將用來描寫敘述正在處理的應用程序窗體的圖形緩衝區屬性標誌的變量flags的Surface.PUSH_BUFFERS位設置爲1。
此外。假設當前正在處理的WindowState對象的成員變量mAttr所描寫敘述的一個WindowManager.LayoutParams對象的成員變量flags的WindowManager.LayoutParams.FLAG_SECURE位不等於0,那麼就說明正在處理的應用程序窗體的界面是安全的。便是受保護的。這時候就需要將用來描寫敘述正在處理的應用程序窗體的圖形緩衝區屬性標誌的變量flags的Surface.SECURE位設置爲1。當一個應用程序窗體的界面是受保護時,SurfaceFlinger服務在運行截屏功能時,就不能把它的界面截取下來。
上述數據準備就緒以後,就可以建立當前正在處理的WindowState對象所描寫敘述的一個應用程序窗體的畫圖表面了,只是在建立以前,還會初始化該WindowState對象的下面成員變量:
--mReportDestroySurface的值被設置爲false。當一個應用程序窗體的畫圖表面被銷燬時,WindowManagerService服務就會將對應的WindowState對象的成員變量mReportDestroySurface的值設置爲true,表示要向該應用程序窗體所執行在應用程序進程發送一個畫圖表面銷燬通知。
--mSurfacePendingDestroy的值被設置爲false。當一個應用程序窗體的畫圖表面正在等待銷燬時,WindowManagerService服務就會將對應的WindowState對象的成員變量mReportDestroySurface的值設置爲true。
--mDrawPending的值被設置爲true。當一個應用程序窗體的畫圖表面處於建立以後並且繪製以前時,WindowManagerService服務就會將對應的WindowState對象的成員變量mDrawPending的值設置爲true,以表示該應用程序窗體的畫圖表面正在等待繪製。
--mCommitDrawPending的值被設置爲false。
當一個應用程序窗體的畫圖表面繪製完畢以後並且可以顯示出來以前時,WindowManagerService服務就會將對應的WindowState對象的成員變量mCommitDrawPending的值設置爲true。以表示該應用程序窗體正在等待顯示。
--mReadyToShow的值被設置爲false。有時候當一個應用程序窗體的畫圖表面繪製完畢並且可以顯示出來以後,由於與該應用程序窗體所關聯的一個Activity組件的其餘窗體還未準備好顯示出來。這時候WindowManagerService服務就會將對應的WindowState對象的成員變量mReadyToShow的值設置爲true,以表示該應用程序窗體需要延遲顯示出來。即需要等到與該應用程序窗體所關聯的一個Activity組件的其餘窗體也可以顯示出來以後再顯示。
--假設成員變量mAppToken的值不等於null。那麼就需要將它所描寫敘述的一個AppWindowToken對象的成員變量allDrawn的值設置爲false。
從前面Android應用程序窗體(Activity)與WindowManagerService服務的鏈接過程分析一文可以知道,一個AppWindowToken對象用來描寫敘述一個Activity組件的,當該AppWindowToken對象的成員變量allDrawn的值等於true時,就表示屬於該Activity組件的所有窗體都已經繪製完畢了。
--mSurfaceShown的值被設置爲false,表示應用程序窗體尚未顯示出來,它是用來顯示調試信息的。
--mSurfaceLayer的值被設置爲0。表示應用程序窗體的Z軸位置。它是用來顯示調試信息的。
--mSurfaceAlpha的值被設置爲1,表示應用程序窗體的透明值。它是用來顯示調試信息的。
--mSurfaceX的值被設置爲0,表示應用程序程序窗體的X軸位置,它是用來顯示調試信息的。
--mSurfaceY的值被設置爲0。表示應用程序程序窗體的Y軸位置,它是用來顯示調試信息的。
--mSurfaceW的值被設置爲w,表示應用程序程序窗體的寬度,它是用來顯示調試信息的。
--mSurfaceH的值被設置爲h。表示應用程序程序窗體的高度。它是用來顯示調試信息的。
一個應用程序窗體的畫圖表面在建立完畢以後,函數就會將獲得的一個Surface對象保存在當前正在處理的WindowState對象的成員變量mSurface中。注意。假設建立畫圖表面失敗,並且從Surface類的構造函數拋出來的異常的類型爲Surface.OutOfResourcesException,那麼就說明系統當前的內存不足了。這時候函數就會調用WindowManagerService類的成員函數reclaimSomeSurfaceMemoryLocked來回收內存。
假設一切正常。那麼函數接下來還會設置當前正在處理的WindowState對象所描寫敘述應用程序窗體的下面屬性:
1. X軸和Y軸位置。前面提到。WindowState類的成員變量mFrame是用來描寫敘述應用程序窗體的位置和大小的,當中。位置就是經過它所描寫敘述的一個Rect對象的成員變量left和top來表示的,它們分別應用窗體在X軸和Y軸上的位置。此外。當一個WindowState對象所描寫敘述的應用程序窗體是一個壁紙窗體時。該WindowState對象的成員變量mXOffset和mYOffset用來描寫敘述壁紙窗體相對當前要顯示的窗體在X軸和Y軸上的偏移量。所以,將WindowState類的成員變量mXOffset的值加上另一個成員變量mFrame所描寫敘述的一個Rect對象的成員變量left的值,就可以獲得一個應用程序窗體在X軸上的位置,相同。將WindowState類的成員變量mYOffset的值加上另一個成員變量mFrame所描寫敘述的一個Rect對象的成員變量top的值。就可以獲得一個應用程序窗體在Y軸上的位置。終於獲得的位置值就分別保存在WindowState類的成員變量mSurfaceX和mSurfaceY,並且會調用WindowState類的成員變量mSurface所描寫敘述的一個Surface對象的成員函數setPosition來將它們設置到SurfaceFlinger服務中去。
2. Z軸位置。
在前面Android應用程序窗體(Activity)與WindowManagerService服務的鏈接過程分析一文中提到,WindowState類的成員變量mAnimLayer用來描寫敘述一個應用程序窗體的Z軸位置,所以。這裏就會先將它保存在WindowState類的另一個成員變量mSurfaceLayer中。而後再調用WindowState類的成員變量mSurface所描寫敘述的一個Surface對象的成員函數setLayer來將它設置到SurfaceFlinger服務中去。
3. 抖動標誌。
假設WindowState類的成員變量mAttr所描寫敘述的一個WindowManager.LayoutParams對象的成員變量flags的WindowManager.LayoutParams.FLAG_DITHER位不等於0,那麼就說明一個應用程序窗體的圖形緩衝區在渲染時。需要進行抖動處理,這時候就會調用WindowState類的成員變量mSurface所描寫敘述的一個Surface對象的成員函數setLayer來將相應的應用程序窗體的圖形緩衝區的屬性標誌的Surface.SURFACE_DITHER位設置爲1。
4. 顯示狀態。
由於當前正在處理的WindowState對象所描寫敘述的一個應用程序窗體的畫圖表面剛剛建立出來,所以。咱們就需要通知SurfaceFlinger服務將它隱藏起來,這是經過調用當前正在處理的WindowState對象的成員變量mSurface所描寫敘述的一個Surface對象的成員變量hide來實現的。
這時候還會將當前正在處理的WindowState對象的成員變量mSurfaceShown和mLastHidden的值分別設置爲false和true。以表示相應的應用程序窗體是處於隱藏狀態的。
注意,爲了不SurfaceFlinger服務每設置一個應用程序窗體屬性就又一次渲染一次系統的UI,上述4個屬性設置需要在一個事務中進行,這樣就可以避免出現界面閃爍。咱們經過調用Surface類的靜態成員函數openTransaction和closeTransaction就可以分別在SurfaceFlinger服務中打開和關閉一個事務。
還有另一個地方需要注意的是。在設置應用程序窗體屬性的過程當中,假設拋出了一個RuntimeException異常。那麼就說明系統當前的內存不足了。這時候函數也會調用WindowManagerService類的成員函數reclaimSomeSurfaceMemoryLocked來回收內存。
接下來,咱們就繼續分析Surface類的構造函數的實現,以便可以瞭解一個應用程序窗體的畫圖表面的具體建立過程。
如下咱們再來看看SurfaceControl的構造函數。主要是調用了nativeCreate函數
public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags) throws OutOfResourcesException { if (session == null) { throw new IllegalArgumentException("session must not be null"); } if (name == null) { throw new IllegalArgumentException("name must not be null"); } if ((flags & SurfaceControl.HIDDEN) == 0) { Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set " + "to ensure that they are not made visible prematurely before " + "all of the surface's properties have been configured. " + "Set the other properties and make the surface visible within " + "a transaction. New surface name: " + name, new Throwable()); } mName = name; mNativeObject = nativeCreate(session, name, w, h, format, flags); if (mNativeObject == 0) { throw new OutOfResourcesException( "Couldn't allocate SurfaceControl native object"); } mCloseGuard.open("release"); }
咱們來看下這個nativeCreate就是經過上篇博客說的SurfaceComposerClient來獲取一個c++層的SurfaceControl
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, jstring nameStr, jint w, jint h, jint format, jint flags) { ScopedUtfChars name(env, nameStr); sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj)); sp<SurfaceControl> surface = client->createSurface( String8(name.c_str()), w, h, format, flags); if (surface == NULL) { jniThrowException(env, OutOfResourcesException, NULL); return 0; } surface->incStrong((void *)nativeCreate); return reinterpret_cast<jlong>(surface.get()); }
而nativeCreate返回的值保存在mNativeObject 中,後面所有一些操做函數都是經過mNativeObject 傳到JNI進行操做的。
public void setLayer(int zorder) { checkNotReleased(); nativeSetLayer(mNativeObject, zorder); } public void setPosition(float x, float y) { checkNotReleased(); nativeSetPosition(mNativeObject, x, y); } public void setSize(int w, int h) { checkNotReleased(); nativeSetSize(mNativeObject, w, h);
在WMS中的Surface是保存在每一個Activity相應的WindowState的winAnimator的mSurfaceControl成員變量中。而Activity的Surface是保存在ViewRootImpl的Surface是經過WMS的outSurface保存在ViewRoot的mSurface中。
因此最後無論在WMS的SurfaceControl仍是ViewRootImpl的Surface最後在c++層用的是同一個對象。
至此,咱們就分析完畢Android應用程序窗體的畫圖表面的建立過程了。經過這個過程咱們就可以知道:
1. 每一個應用程序窗體都相應有兩個Java層的Surface對象,當中一個是在WindowManagerService服務這一側建立的。而另一個是在應用程序進程這一側建立的。
2. 在WindowManagerService服務這一側建立的Java層的Surface對象在C++層關聯有一個SurfaceControl對象,用來設置應用窗體的屬性,好比,大小和位置等。
3. 在應用程序進程這一側建立的ava層的Surface對象在C++層關聯有一個Surface對象。用來繪製應用程序窗品的UI。
理解上述三個結論對理解Android應用程序窗體的實現框架以及WindowManagerService服務的實現都很重要。
一個應用程序窗體的畫圖表面在建立完畢以後,接下來應用程序進程就可以在上面繪製它的UI了。