android6.0 Activity(四) Surface創建

在上一篇博客中,我們分析瞭應用程序窗口連接到WindowManagerService服務的過程。在這個過程中,WindowManagerService服務會為應用程序窗口創建過一個到SurfaceFlinger服務的連接。有瞭這個連接之後,WindowManagerService服務就可以為應用程序窗口創建繪圖表面瞭,以便可以用來渲染窗口的UI。在本文中,我們就詳細分析應用程序窗口的繪圖表面的創建過程。

一、Surface創建過程介紹

每一個在C++層實現的應用程序窗口都需要有一個繪圖表面,然後才可以將自己的UI表現出來。這個繪圖表面是需要由應用程序進程請求SurfaceFlinger服務來創建的,在SurfaceFlinger服務內部使用一個Layer對象來描述,同時,SurfaceFlinger服務會返回一個實現瞭ISurface接口的Binder本地對象給應用程序進程,於是,應用程序進程就可以獲得一個實現瞭ISurface接口的Binder代理對象。有瞭這個實現瞭ISurface接口的Binder代理對象之後,在C++層實現的應用程序窗口就可以請求SurfaceFlinger服務分配圖形緩沖區以及渲染已經填充好UI數據的圖形緩沖區瞭。

對於在Java層實現的Android應用程序窗口來說,它也需要請求SurfaceFlinger服務為它創建繪圖表面,這個繪圖表面使用一個Surface對象來描述。由於在Java層實現的Android應用程序窗口還要接受WindowManagerService服務管理,因此,它的繪圖表面的創建流程就會比在C++層實現的應用程序窗口復雜一些。具體來說,就是在在Java層實現的Android應用程序窗口的繪圖表面是通過兩個Surface對象來描述,一個是在應用程序進程這一側創建的,另一個是在WindowManagerService服務這一側創建的,它們對應於SurfaceFlinger服務這一側的同一個Layer對象,如圖所示:

在應用程序進程這一側,每一個應用程序窗口,即每一個Activity組件,都有一個關聯的Surface對象,這個Surface對象是保在在一個關聯的ViewRootImpl對象的成員變量mSurface中的。這裡我們隻關註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開始,分析應用程序窗口的繪圖表面的創建過程,如圖所示:

二、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的進程間通信請求的。

 

2.1 WMS中創建SurfaceControl

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類的構造函數的實現,以便可以瞭解一個應用程序窗口的繪圖表面的詳細創建過程。

 

2.2 SurfaceControl在JNI層創建瞭SurfaceControl C++層對象那個

下面我們再來看看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 client(android_view_SurfaceSession_getClient(env, sessionObj));
    sp 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(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瞭。

發佈留言