Surface的创建过程分析
WindowManagerService服務會為應用程序窗口創(chuàng)建一個到SurfaceFlinger服務的連接。有了這個連接之后,WindowManagerService服務就可以為應用程序窗口創(chuàng)建繪圖表面了,以便可以用來渲染窗口的UI。
每一個在C++層實現(xiàn)的應用程序窗口都需要有一個繪圖表面,然后才可以將自己的UI表現(xiàn)出來。這個繪圖表面是需要由應用程序進程請求SurfaceFlinger服務來創(chuàng)建的,在SurfaceFlinger服務內部使用一個Layer對象來描述,同時,SurfaceFlinger服務會返回一個實現(xiàn)了ISurface接口的Binder本地對象給應用程序進程,于是,應用程序進程就可以獲得一個實現(xiàn)了ISurface接口的Binder代理對象。有了這個實現(xiàn)了ISurface接口的Binder代理對象之后,在C++層實現(xiàn)的應用程序窗口就可以請求SurfaceFlinger服務分配圖形緩沖區(qū)以及渲染已經填充好UI數(shù)據的圖形緩沖區(qū)了。
對于在Java層實現(xiàn)的Android應用程序窗口來說,它也需要請求SurfaceFlinger服務為它創(chuàng)建繪圖表面,這個繪圖表面使用一個Surface對象來描述。由于在Java層實現(xiàn)的Android應用程序窗口還要接受WindowManagerService服務管理,因此,它的繪圖表面的創(chuàng)建流程就會比在C++層實現(xiàn)的應用程序窗口復雜一些。具體來說,就是在在Java層實現(xiàn)的Android應用程序窗口的繪圖表面是通過兩個Surface對象來描述,一個是在應用程序進程這一側創(chuàng)建的,另一個是在WindowManagerService服務這一側創(chuàng)建的,它們對應于SurfaceFlinger服務這一側的同一個Layer對象,如圖所示:
在應用程序進程這一側,每一個應用程序窗口,即每一個Activity組件,都有一個關聯(lián)的Surface對象,這個Surface對象是保在在一個關聯(lián)的ViewRoot對象的成員變量mSurface中的,如圖所示:
在應用程序進程這一側,每一個Java層的Surface對都對應有一個C++層的Surface對象,并且后者的地址值保存在前者的成員變量mNativeSurface中。在WindowManagerService服務這一側,每一個應用程序窗口,即每一個Activity組件,都有一個對應的WindowState對象,這個WindowState對象的成員變量mSurface同樣是指向了一個Surface對象,如圖所示:
在WindowManagerService服務這一側,每一個Java層的Surface對都對應有一個C++層的SurfaceControl對象,并且后者的地址值保存在前者的成員變量mSurfaceControl中。
一個應用程序窗口分別位于應用程序進程和WindowManagerService服務中的兩個Surface對象有什么區(qū)別呢?雖然它們都是用來操作位于SurfaceFlinger服務中的同一個Layer對象的,不過,它們的操作方式卻不一樣。具體來說,就是位于應用程序進程這一側的Surface對象負責繪制應用程序窗口的UI,即往應用程序窗口的圖形緩沖區(qū)填充UI數(shù)據,而位于WindowManagerService服務這一側的Surface對象負責設置應用程序窗口的屬性,例如位置、大小等屬性。這兩種不同的操作方式分別是通過C++層的Surface對象和SurfaceControl對象來完成的,因此,位于應用程序進程和WindowManagerService服務中的兩個Surface對象的用法是有區(qū)別的。之所以會有這樣的區(qū)別,是因為繪制應用程序窗口是獨立的,由應用程序進程來完即可,而設置應用程序窗口的屬性卻需要全局考慮,即需要由WindowManagerService服務來統(tǒng)籌安排,例如,一個應用程序窗口的Z軸坐標大小要考慮它到的窗口類型以及它與系統(tǒng)中的其它窗口的關系。
由于一個應用程序窗口對應有兩個Surface對象,那么它們是如何創(chuàng)建出來的呢?簡單地說,就是按照以下步驟來創(chuàng)建:
那么應用程序窗口的繪圖表面又是什么時候創(chuàng)建的呢?一般是在不存在的時候就創(chuàng)建,因為應用程序窗口在運行的過程中,它的繪圖表面會根據需要來銷毀以及重新創(chuàng)建的,例如,應用程序窗口在第一次顯示的時候,就會請求WindowManagerService服務為其創(chuàng)建繪制表面。當一個應用程序窗口被激活并且它的視圖對象創(chuàng)建完成之后,應用程序進程就會調用與其所關聯(lián)的一個ViewRoot對象的成員函數(shù)requestLayout來請求對其UI進行布局以及顯示。由于這時候應用程序窗口的繪圖表面尚未創(chuàng)建,因此,ViewRoot類的成員函數(shù)requestLayout就會請求WindowManagerService服務來創(chuàng)建繪圖表面。接下來,我們就從ViewRoot類的成員函數(shù)requestLayout開始,分析應用程序窗口的繪圖表面的創(chuàng)建過程。
Step 1. ViewRoot.requestLayout
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... boolean mLayoutRequested; ...... public void requestLayout() { checkThread(); mLayoutRequested = true; scheduleTraversals(); } ...... }ViewRoot類的成員函數(shù)requestLayout首先調用另外一個成員函數(shù)checkThread來檢查當前線程是否就是創(chuàng)建當前正在處理的ViewRoot對象的線程。如果不是的話,那么ViewRoot類的成員函數(shù)checkThread就會拋出一個異常出來。ViewRoot類是從Handler類繼承下來的,用來處理應用程序窗口的UI布局和渲染等消息。由于這些消息都是與Ui相關的,因此它們就需要在UI線程中處理,這樣我們就可以推斷出當前正在處理的ViewRoot對象是要應用程序進程的UI線程中創(chuàng)建的。進一步地,我們就可以推斷出ViewRoot類的成員函數(shù)checkThread實際上就是用來檢查當前線程是否是應用程序進程的UI線程,如果不是的話,它就會拋出一個異常出來。
通過了上述檢查之后,ViewRoot類的成員函數(shù)requestLayout首先將其成員變量mLayoutRequested的值設置為true,表示應用程序進程的UI線程正在被請求執(zhí)行一個UI布局操作,接著再調用另外一個成員函數(shù)scheduleTraversals來繼續(xù)執(zhí)行UI布局的操作。
Step 2. ViewRoot.scheduleTraversals
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... boolean mTraversalScheduled; ...... public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; sendEmptyMessage(DO_TRAVERSAL); } } ...... }ViewRoot類的成員變量mTraversalScheduled用來表示應用程序進程的UI線程是否已經調度了一個DO_TRAVERSAL消息。如果已經調度了的話,它的值就會等于true。在這種情況下, ViewRoot類的成員函數(shù)scheduleTraversals就什么也不做,否則的話,它就會首先將成員變量mTraversalScheduled的值設置為true,然后再調用從父類Handler繼承下來的成員函數(shù)sendEmptyMessage來往應用程序進程的UI線程發(fā)送一個DO_TRAVERSAL消息。這個類型為DO_TRAVERSAL的消息是由ViewRoot類的成員函數(shù)performTraversals來處理的,因此,接下來我們就繼續(xù)分析ViewRoot類的成員函數(shù)performTraversals的實現(xiàn)。
Step 3. ViewRoot.performTraversals
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... View mView; ...... boolean mLayoutRequested; boolean mFirst; ...... boolean mFullRedrawNeeded; ...... private final Surface mSurface = new Surface(); ...... private void performTraversals() { ...... final View host = mView; ...... mTraversalScheduled = false; ...... boolean fullRedrawNeeded = mFullRedrawNeeded; boolean newSurface = false; ...... if (mLayoutRequested) { ...... host.measure(childWidthMeasureSpec, childHeightMeasureSpec); ....... } ...... int relayoutResult = 0; if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null) { ...... boolean hadSurface = mSurface.isValid(); try { ...... relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); ...... if (!hadSurface) { if (mSurface.isValid()) { ...... newSurface = true; fullRedrawNeeded = true; ...... } } ...... } catch (RemoteException e) { } ...... } final boolean didLayout = mLayoutRequested; ...... if (didLayout) { mLayoutRequested = false; ...... host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight); ...... } ...... mFirst = false; ...... boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw(); if (!cancelDraw && !newSurface) { mFullRedrawNeeded = false; draw(fullRedrawNeeded); ...... } else { ...... // Try again scheduleTraversals(); } } ...... }在分析ViewRoot類的成員函數(shù)performTraversals的實現(xiàn)框架之前,我們首先了解ViewRoot類的以下五個成員變量:
- mView:它的類型為View,但它實際上指向的是一個DecorView對象,用來描述應用程序窗口的頂級視圖
- mLayoutRequested:這是一個布爾變量,用來描述應用程序進程的UI線程是否需要正在被請求執(zhí)行一個UI布局操作。
- mFirst:這是一個布爾變量,用來描述應用程序進程的UI線程是否第一次處理一個應用程序窗口的UI。
- mFullRedrawNeeded:這是一個布爾變量,用來描述應用程序進程的UI線程是否需要將一個應用程序窗口的全部區(qū)域都重新繪制。
- mSurface:它指向一個Java層的Surface對象,用來描述一個應用程序窗口的繪圖表面。
注意,成員變量mSurface所指向的Surface對象在創(chuàng)建的時候,還沒有在C++層有一個關聯(lián)的Surface對象,因此,這時候它描述的就是一個無效的繪圖表面。另外,這個Surface對象在應用程序窗口運行的過程中,也會可能被銷毀,因此,這時候它描述的繪圖表面也會變得無效。在上述兩種情況中,我們都需要請求WindowManagerService服務來為當前正在處理的應用程序窗口創(chuàng)建有一個有效的繪圖表面,以便可以在上面渲染UI。
理解了上述五個成員變量之后,我們就可以分析ViewRoot類的成員函數(shù)performTraversals的實現(xiàn)框架了,如下所示:
這樣,我們就分析完成ViewRoot類的成員函數(shù)performTraversals的實現(xiàn)框架了,接下來我們就繼續(xù)分析ViewRoot類的成員函數(shù)relayoutWindow的實現(xiàn),以便可以看到當前正在處理的應用程序窗口的繪圖表面是如何創(chuàng)建的。
Step 4. ViewRoot.relayoutWindow
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... static IWindowSession sWindowSession; ...... final W mWindow; ...... private final Surface mSurface = new Surface(); ...... private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { ...... int relayoutResult = sWindowSession.relayout( mWindow, params, (int) (mView.mMeasuredWidth * appScale + 0.5f), (int) (mView.mMeasuredHeight * appScale + 0.5f), viewVisibility, insetsPending, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mPendingConfiguration, mSurface); ...... return relayoutResult; } ...... }ViewRoot類的成員函數(shù)relayoutWindow調用靜態(tài)成員變量sWindowSession所描述的一個實現(xiàn)了IWindowSession接口的Binder代理對象的成員函數(shù)relayout來請求WindowManagerService服務對成員變量mWindow所描述的一個應用程序窗口的UI進行重新布局,同時,還會將成員變量mSurface所描述的一個Surface對象傳遞給WindowManagerService服務,以便WindowManagerService服務可以根據需要來重新創(chuàng)建一個繪圖表面給成員變量mWindow所描述的一個應用程序窗口使用。
實現(xiàn)了IWindowSession接口的Binder代理對象是由IWindowSession.Stub.Proxy類來描述的,接下來我們就繼續(xù)分析它的成員函數(shù)relayout的實現(xiàn)。
Step 5. IWindowSession.Stub.Proxy.relayout
IWindowSession接口是使用AIDL語言來描述的,如下所示:
interface IWindowSession { ...... int relayout(IWindow window, in WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, out Rect outFrame, out Rect outContentInsets, out Rect outVisibleInsets, out Configuration outConfig, out Surface outSurface); ...... }使用AIDL語言來描述的IWindowSession接口被編譯后,就會生成一個使用Java語言來描述的IWindowSession.Stub.Proxy類,它的成員函數(shù)relayout的實現(xiàn)如下所示:
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類的成員函數(shù)relayout首先將從前面?zhèn)鬟M來的各個參數(shù)寫入到Parcel對象_data中,接著再通過其成員變量mRemote所描述的一個Binder代理對象向運行在WindowManagerService服務內部的一個Session對象發(fā)送一個類型為TRANSACTION_relayout的進程間通信請求,其中,這個Session對象是用來描述從當前應用程序進程到WindowManagerService服務的一個連接的。
當運行在WindowManagerService服務內部的Session對象處理完成當前應用程序進程發(fā)送過來的類型為TRANSACTION_relayout的進程間通信請求之后,就會將處理結果寫入到Parcel對象_reply中,并且將這個Parcel對象_reply返回給當前應用程序進程處理。返回結果包含了一系列與參數(shù)window所描述的應用程序窗口相關的參數(shù),如下所示:
這里我們只關注從WindowManagerService服務返回來的窗口繪圖表面是如何保存到輸出參數(shù)outSurface中的,即關注Surface類的成員函數(shù)readFromParcel的實現(xiàn)。輸出參數(shù)outSurface描述的便是當前正在處理的應用程序窗口的繪圖表面,將WindowManagerService服務返回來的窗口繪圖表面保存在它里面,就相當于是為當前正在處理的應用程序窗口創(chuàng)建了一個繪圖表面。
在分析Surface類的成員函數(shù)readFromParcel的實現(xiàn)之前,我們先分析Session類的成員函數(shù)relayout的實現(xiàn),因為它是用來處理類型為TRANSACTION_relayout的進程間通信請求的。
Step 6. Session.relayout
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { ...... public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, boolean insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); int res = relayoutWindow(this, window, attrs, requestedWidth, requestedHeight, viewFlags, insetsPending, outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); return res; } ...... } ...... }Session類的成員函數(shù)relayout調用了外部類WindowManagerService的成員函數(shù)relayoutWindow來對參數(shù)參數(shù)window所描述的一個應用程序窗口的UI進行布局,接下來我們就繼續(xù)分析WindowManagerService類的成員函數(shù)relayoutWindow的實現(xiàn)。
Step 7. WindowManagerService.relayoutWindow
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public int relayoutWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { ...... synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } if (viewVisibility == View.VISIBLE && (win.mAppToken == null || !win.mAppToken.clientHidden)) { ...... try { Surface surface = win.createSurfaceLocked(); if (surface != null) { outSurface.copyFrom(surface); ...... } else { // For some reason there isn't a surface. Clear the // caller's object so they see the same state. outSurface.release(); } } catch (Exception e) { ...... return 0; } ...... } ...... } return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0); } ...... }簡單來說,WindowManagerService類的成員函數(shù)relayoutWindow根據應用程序進程傳遞過來的一系列數(shù)據來重新設置由參數(shù)client所描述的一個應用程序窗口的大小和可見性等信息,而當一個應用程序窗口的大小或者可見性發(fā)生變化之后,系統(tǒng)中當前獲得焦點的窗口,以及輸入法窗口和壁紙窗口等都可能會發(fā)生變化,而且也會對其它窗口產生影響,因此,這時候WindowManagerService類的成員函數(shù)relayoutWindow就會對系統(tǒng)中的窗口的布局進行重新調整。
現(xiàn)在,我們就主要分析參數(shù)client所描述的一個應用程序窗口的繪圖表面的創(chuàng)建過程。WindowManagerService類的成員函數(shù)relayoutWindow首先獲得與參數(shù)client所對應的一個WindowState對象win,這是通過調用WindowManagerService類的成員函數(shù)windowForClientLocked來實現(xiàn)的。如果這個對應的WindowState對象win不存在,那么就說明應用程序進程所請求處理的應用程序窗口不存在,這時候WindowManagerService類的成員函數(shù)relayoutWindow就直接返回一個0值給應用程序進程。
WindowManagerService類的成員函數(shù)relayoutWindow接下來判斷參數(shù)client所描述的一個應用程序窗口是否是可見的。一個窗口只有在可見的情況下,WindowManagerService服務才會為它創(chuàng)建一個繪圖表面。 一個窗口是否可見由以下兩個條件決定:
注意,當WindowState對象win的成員變量mAppToken等于null時,只要滿足條件1就可以了,因為這時候參數(shù)client所描述的窗口不是一個Activity組件窗口,它的可見性不像Activity組件窗口一樣受到Activity組件的可見性影響。
我們假設參數(shù)client所描述的是一個應用程序窗口,并且這個應用程序窗口是可見的,那么WindowManagerService類的成員函數(shù)relayoutWindow接下來就會調用WindowState對象win的成員函數(shù)createSurfaceLocked來為它創(chuàng)建一個繪圖表面。如果這個繪圖表面能創(chuàng)建成功,那么WindowManagerService類的成員函數(shù)relayoutWindow就會將它的內容拷貝到輸出參數(shù)outSource所描述的一個Surface對象去,以便可以將它返回給應用程序進程處理。另一方面,如果這個繪圖表面不能創(chuàng)建成功,那么WindowManagerService類的成員函數(shù)relayoutWindow就會將輸出參數(shù)outSource所描述的一個Surface對象的內容釋放掉,以便應用程序進程知道該Surface對象所描述的繪圖表面已經失效了。
接下來,我們就繼續(xù)分析WindowState類的成員函數(shù)createSurfaceLocked的實現(xiàn),以便可以了解一個應用程序窗口的繪圖表面的創(chuàng)建過程。
Step 8. WindowState.createSurfaceLocked
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class WindowState implements WindowManagerPolicy.WindowState { ...... Surface mSurface; ...... Surface createSurfaceLocked() { if (mSurface == null) { mReportDestroySurface = false; mSurfacePendingDestroy = false; mDrawPending = true; mCommitDrawPending = false; mReadyToShow = false; if (mAppToken != null) { mAppToken.allDrawn = false; } int flags = 0; if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) { flags |= Surface.PUSH_BUFFERS; } if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) { flags |= Surface.SECURE; } ...... int w = mFrame.width(); int h = mFrame.height(); if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { // for a scaled surface, we always want the requested // size. w = mRequestedWidth; h = mRequestedHeight; } // Something is wrong and SurfaceFlinger will not like this, // try to revert to sane values if (w <= 0) w = 1; if (h <= 0) h = 1; mSurfaceShown = false; mSurfaceLayer = 0; mSurfaceAlpha = 1; mSurfaceX = 0; mSurfaceY = 0; mSurfaceW = w; mSurfaceH = h; try { mSurface = new Surface( mSession.mSurfaceSession, mSession.mPid, mAttrs.getTitle().toString(), 0, w, h, mAttrs.format, flags); ...... } catch (Surface.OutOfResourcesException e) { ...... reclaimSomeSurfaceMemoryLocked(this, "create"); return null; } catch (Exception e) { ...... return null; } Surface.openTransaction(); try { try { mSurfaceX = mFrame.left + mXOffset; mSurfaceY = mFrame.top + mYOffset; mSurface.setPosition(mSurfaceX, mSurfaceY); mSurfaceLayer = mAnimLayer; mSurface.setLayer(mAnimLayer); mSurfaceShown = false; mSurface.hide(); if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) { ...... mSurface.setFlags(Surface.SURFACE_DITHER, Surface.SURFACE_DITHER); } } catch (RuntimeException e) { ...... reclaimSomeSurfaceMemoryLocked(this, "create-init"); } mLastHidden = true; } finally { ...... Surface.closeTransaction(); } ...... } return mSurface; } ...... } ...... }在創(chuàng)建一個應用程序窗口的繪圖表面之前,我們需要知道以下數(shù)據:
第1個和第2個數(shù)據可以通過當前正在處理的WindowState對象的成員變量mSession所描述的一個Session對象的成員變量mPid和mSurfaceSession來獲得;第3個和第4個數(shù)據可以通過當前正在處理的WindowState對象的成員變量mAttr所描述的一個WindowManager.LayoutParams對象的成員函數(shù)getTitle以及成員變量format來獲得;接下來我們就分析后面3個數(shù)據是如何獲得的。
WindowState類的成員變量mFrame的類型為Rect,它用來描述應用程序窗口的位置和大小,它們是由WindowManagerService服務根據屏幕大小以及其它屬性計算出來的,因此,通過調用過它的成員函數(shù)width和height就可以得到要創(chuàng)建繪圖表面的應用程序窗口的寬度和高度,并且保存在變量w和h中。但是,當當前正在處理的WindowState對象的成員變量mAttr所描述的一個WindowManager.LayoutParams對象的成員變量flags的LayoutParams.FLAG_SCALED位不等于0時,就說明應用程序進程指定了該應用程序窗口的大小,這時候指定的應用程序窗口的寬度和高度就保存在WindowState類的成員變量mRequestedWidth和mRequestedHeight中,因此,我們就需要將當前正在處理的WindowState對象的成員變量mRequestedWidth和mRequestedHeight的值分別保存在變量w和h中。經過上述兩步計算之后,如果得到的變量w和h等于0,那么就說明當前正在處理的WindowState對象所描述的應用程序窗口的大小還沒有經過計算,或者還沒有被指定過,這時候就需要將它們的值設置為1,避免接下來創(chuàng)建一個大小為0的繪圖表面。
如果當前正在處理的WindowState對象的成員變量mAttr所描述的一個WindowManager.LayoutParams對象的成員變量memoryType的值等于MEMORY_TYPE_PUSH_BUFFERS,那么就說明正在處理的應用程序窗口不擁有專屬的圖形緩沖區(qū),這時候就需要將用來描述正在處理的應用程序窗口的圖形緩沖區(qū)屬性標志的變量flags的Surface.PUSH_BUFFERS位設置為1。
此外,如果當前正在處理的WindowState對象的成員變量mAttr所描述的一個WindowManager.LayoutParams對象的成員變量flags的WindowManager.LayoutParams.FLAG_SECURE位不等于0,那么就說明正在處理的應用程序窗口的界面是安全的,即是受保護的,這時候就需要將用來描述正在處理的應用程序窗口的圖形緩沖區(qū)屬性標志的變量flags的Surface.SECURE位設置為1。當一個應用程序窗口的界面是受保護時,SurfaceFlinger服務在執(zhí)行截屏功能時,就不能把它的界面截取下來。
上述數(shù)據準備就緒之后,就可以創(chuàng)建當前正在處理的WindowState對象所描述的一個應用程序窗口的繪圖表面了,不過在創(chuàng)建之前,還會初始化該WindowState對象的以下成員變量:
- mReportDestroySurface的值被設置為false。當一個應用程序窗口的繪圖表面被銷毀時,WindowManagerService服務就會將相應的WindowState對象的成員變量mReportDestroySurface的值設置為true,表示要向該應用程序窗口所運行在應用程序進程發(fā)送一個繪圖表面銷毀通知。
- mSurfacePendingDestroy的值被設置為false。當一個應用程序窗口的繪圖表面正在等待銷毀時,WindowManagerService服務就會將相應的WindowState對象的成員變量mReportDestroySurface的值設置為true。
- mDrawPending的值被設置為true。當一個應用程序窗口的繪圖表面處于創(chuàng)建之后并且繪制之前時,WindowManagerService服務就會將相應的WindowState對象的成員變量mDrawPending的值設置為true,以表示該應用程序窗口的繪圖表面正在等待繪制。
- mCommitDrawPending的值被設置為false。當一個應用程序窗口的繪圖表面繪制完成之后并且可以顯示出來之前時,WindowManagerService服務就會將相應的WindowState對象的成員變量mCommitDrawPending的值設置為true,以表示該應用程序窗口正在等待顯示。
- mReadyToShow的值被設置為false。有時候當一個應用程序窗口的繪圖表面繪制完成并且可以顯示出來之后,由于與該應用程序窗口所關聯(lián)的一個Activity組件的其它窗口還未準備好顯示出來,這時候WindowManagerService服務就會將相應的WindowState對象的成員變量mReadyToShow的值設置為true,以表示該應用程序窗口需要延遲顯示出來,即需要等到與該應用程序窗口所關聯(lián)的一個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,表示應用程序程序窗口的高度,它是用來顯示調試信息的。
一個應用程序窗口的繪圖表面在創(chuàng)建完成之后,函數(shù)就會將得到的一個Surface對象保存在當前正在處理的WindowState對象的成員變量mSurface中。注意,如果創(chuàng)建繪圖表面失敗,并且從Surface類的構造函數(shù)拋出來的異常的類型為Surface.OutOfResourcesException,那么就說明系統(tǒng)當前的內存不足了,這時候函數(shù)就會調用WindowManagerService類的成員函數(shù)reclaimSomeSurfaceMemoryLocked來回收內存。
如果一切正常,那么函數(shù)接下來還會設置當前正在處理的WindowState對象所描述應用程序窗口的以下屬性:
注意,為了避免SurfaceFlinger服務每設置一個應用程序窗口屬性就重新渲染一次系統(tǒng)的UI,上述4個屬性設置需要在一個事務中進行,這樣就可以避免出現(xiàn)界面閃爍。我們通過調用Surface類的靜態(tài)成員函數(shù)openTransaction和closeTransaction就可以分別在SurfaceFlinger服務中打開和關閉一個事務。
還有另外一個地方需要注意的是,在設置應用程序窗口屬性的過程中,如果拋出了一個RuntimeException異常,那么就說明系統(tǒng)當前的內存不足了,這時候函數(shù)也會調用WindowManagerService類的成員函數(shù)reclaimSomeSurfaceMemoryLocked來回收內存。
接下來,我們就繼續(xù)分析Surface類的構造函數(shù)的實現(xiàn),以便可以了解一個應用程序窗口的繪圖表面的詳細創(chuàng)建過程。
Step 9. new Surface
public class Surface implements Parcelable { ...... private int mSurfaceControl; ...... private Canvas mCanvas; ...... private String mName; ...... public Surface(SurfaceSession s, int pid, String name, int display, int w, int h, int format, int flags) throws OutOfResourcesException { ...... mCanvas = new CompatibleCanvas(); init(s,pid,name,display,w,h,format,flags); mName = name; } ...... private native void init(SurfaceSession s, int pid, String name, int display, int w, int h, int format, int flags) throws OutOfResourcesException; ...... }Surface類有三個成員變量mSurfaceControl、mCanvas和mName,它們的類型分別是int、Canvas和mName,其中,mSurfaceControl保存的是在C++層的一個SurfaceControl對象的地址值,mCanvas用來描述一塊類型為CompatibleCanvas的畫布,mName用來描述當前正在創(chuàng)建的一個繪圖表面的名稱。畫布是真正用來繪制UI的地方,不過由于現(xiàn)在正在創(chuàng)建的繪圖表面是在WindowManagerService服務這一側使用的,而WindowManagerService服務不會去繪制應用程序窗口的UI,它只會去設置應用程序窗口的屬性,因此,這里創(chuàng)建的畫布實際上沒有什么作用,我們主要關注與成員變量mSurfaceControl所關聯(lián)的C++層的SurfaceControl對象是如何創(chuàng)建的。
Surface類的構造函數(shù)是通過調用另外一個成員函數(shù)init來創(chuàng)建與成員變量mSurfaceControl所關聯(lián)的C++層的SurfaceControl對象的。Surface類的成員函數(shù)init是一個JNI方法,它是由C++層的函數(shù)Surface_init來實現(xiàn)的,如下所示:
static void Surface_init( JNIEnv* env, jobject clazz, jobject session, jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags) { if (session == NULL) { doThrow(env, "java/lang/NullPointerException"); return; } SurfaceComposerClient* client = (SurfaceComposerClient*)env->GetIntField(session, sso.client); sp<SurfaceControl> surface; if (jname == NULL) { surface = client->createSurface(pid, dpy, w, h, format, flags); } else { const jchar* str = env->GetStringCritical(jname, 0); const String8 name(str, env->GetStringLength(jname)); env->ReleaseStringCritical(jname, str); surface = client->createSurface(pid, name, dpy, w, h, format, flags); } if (surface == 0) { doThrow(env, OutOfResourcesException); return; } setSurfaceControl(env, clazz, surface); }參數(shù)session指向了在Java層所創(chuàng)建的一個SurfaceSession對象。Java層的SurfaceSession對象有一個成員變量mClient,它指向了在C++層中的一個SurfaceComposerClient對象。C++層的SurfaceComposerClient對象可以用來請求SurfaceFlinger服務為應用程序窗口創(chuàng)建繪圖表面,即創(chuàng)建一個Layer對象。
因此,函數(shù)首先將參數(shù)session所指向的一個Java層的SurfaceSession對象的成員變量mClient轉換成一個SurfaceComposerClient對象,然后再調用這個SurfaceComposerClient對象的成員函數(shù)createSurface來請求SurfaceFlinger服務來為參數(shù)clazz所描述的一個Java層的Surface對象所關聯(lián)的應用程序窗口創(chuàng)建一個Layer對象。SurfaceFlinger服務創(chuàng)建完成這個Layer對象之后,就會將該Layer對象內部的一個實現(xiàn)了ISurface接口的SurfaceLayer對象返回給函數(shù),于是,函數(shù)就可以獲得一個實現(xiàn)了ISurface接口的Binder代理對象。這個實現(xiàn)了ISurface接口的Binder代理對象被封裝在C++層的一個SurfaceControl對象surface中。
注意,sso是一個全局變量,它是一個類型為sso_t的結構體,它的定義如下所示:
struct sso_t { jfieldID client; }; static sso_t sso;它的成員函數(shù)client用來描述Java層中的SurfaceSession類的成員變量mClient在類中的偏移量,因此,函數(shù)Surface_init通過這個偏移量就可以訪問參數(shù)session所指向的一個SurfaceSession對象的成員變量mClient的值,從而獲得一個SurfaceComposerClient對象。
另外一個需要注意的地方是,當參數(shù)name的值等于null時,函數(shù)Surface_init調用前面所獲得一個SurfaceComposerClient對象的六個參數(shù)版本的成員函數(shù)createSurface來請求SurfaceFlinger服務創(chuàng)建一個Layer對象,否則的話,就調用七個參數(shù)版本的成員函數(shù)createSurface來請求SurfaceFlinger服務創(chuàng)建一個Layer對象。
得到了SurfaceControl對象surface之后,函數(shù)Surface_init接下來繼續(xù)調用另外一個函數(shù)setSurfaceControl來它的地址值保存在參數(shù)clazz所指向的一個Java層的Surface對象的成員變量mSurfaceControl中,如下所示:
static void setSurfaceControl(JNIEnv* env, jobject clazz, const sp<SurfaceControl>& surface) { SurfaceControl* const p = (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl); if (surface.get()) { surface->incStrong(clazz); } if (p) { p->decStrong(clazz); } env->SetIntField(clazz, so.surfaceControl, (int)surface.get()); } 這個函數(shù)定義在文件frameworks/base/core/jni/android_view_Surface.cpp中。在分析函數(shù)setSurfaceControl的實現(xiàn)之前,我們先分析全局變量so的定義,如下所示: [cpp] view plain copy struct so_t { jfieldID surfaceControl; jfieldID surface; jfieldID saveCount; jfieldID canvas; }; static so_t so;它是一個類型為so_t的結構體。結構體so_t有四個成員變量surfaceControl、surface、saveCount和canvas,它們分別用來描述Java層的Surface類的四個成員變量mSurfaceControl、mNativeSurface、mSaveCount和mCanvas在類中的偏移量。
回到函數(shù)setSurfaceControl中,它首先通過結構體so的成員變量surfaceControl來獲得參數(shù)clazz所指向的一個Java層的Surface對象的成員變量mSurfaceControl所關聯(lián)的一個C++層的SurfaceControl對象。如果這個SurfaceControl對象存在,那么變量p的值就不會等于null,在這種情況下,就需要調用它的成員函數(shù)decStrong來減少它的強引用計數(shù),因為接下來參數(shù)clazz所指向的一個Java層的Surface對象不再通過成員變量mSurfaceControl來引用它了。
另一方面,函數(shù)setSurfaceControl需要增加參數(shù)surface所指向的一個C++層的SurfaceControl對象的強引用計數(shù),即調用參數(shù)surface所指向的一個C++層的SurfaceControl對象的成員函數(shù)incStrong,因為接下來參數(shù)clazz所指向的一個Java層的Surface對象要通過成員變量mSurfaceControl來引用它,即將它的地址值保存在參數(shù)clazz所指向的一個Java層的Surface對象的成員變量mSurfaceControl中。
這一步執(zhí)行完成之后,沿著調用路徑層層返回,回到前面的Step 8中,即WindowState類的成員函數(shù)createSurfaceLocked中,這時候一個繪圖表面就創(chuàng)建完成了。這個繪圖表面最終會返回給請求創(chuàng)建它的應用程序進程,即前面的Step 5,也就是IWindowSession.Stub.Proxy類的成員函數(shù)relayout。
IWindowSession.Stub.Proxy類的成員函數(shù)relayout獲得了從WindowManagerService服務返回來的一個繪圖表面,即一個Java層的Surface對象之后,就會將它的內容拷貝到參數(shù)outSurface所描述的另外一個Java層的Surface對象中,這里通過調用Surface類的成員函數(shù)readFromParcel來實現(xiàn)的。注意,參數(shù)outSurface所描述的這個Java層的Surface對象是在應用程序進程這一側創(chuàng)建的,它的作用是用來繪制應用程序窗品的UI。
接下來,我們就繼續(xù)分析Surface類的成員函數(shù)readFromParcel的實現(xiàn),以便可以了解在應用程序進程這一側的Surface對象是如何創(chuàng)建的。
Step 10. Surface.readFromParcel
public class Surface implements Parcelable { ...... public native void readFromParcel(Parcel source); ...... }Surface類的成員函數(shù)readFromParcel是一個JNI方法,它是由C++層的函數(shù)Surface_readFromParcel來實現(xiàn)的,如下所示:
static void Surface_readFromParcel( JNIEnv* env, jobject clazz, jobject argParcel) { Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel); if (parcel == NULL) { doThrow(env, "java/lang/NullPointerException", NULL); return; } sp<Surface> sur(Surface::readFromParcel(*parcel)); setSurface(env, clazz, sur); }參數(shù)argParcel所指向的一個Parcel對象的當前位置保存的是一個Java層的Surface對象的內容,函數(shù)readFromParcel首先調用C++層的Surface類的成員函數(shù)readFromParcel來將這些內容封裝成一個C++層的Surface對象,如下所示:
sp<Surface> Surface::readFromParcel(const Parcel& data) { Mutex::Autolock _l(sCachedSurfacesLock); sp<IBinder> binder(data.readStrongBinder()); sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote(); if (surface == 0) { surface = new Surface(data, binder); sCachedSurfaces.add(binder, surface); } if (surface->mSurface == 0) { surface = 0; } cleanCachedSurfacesLocked(); return surface; }參數(shù)data所指向的一個Parcel對象的當前位置保存的是一個Binder代理對象,這個Binder代理對象實現(xiàn)了ISurface接口,它所引用的Binder本地對象就是在前面的Step 9中WindowManagerService服務請求SurfaceFlinger服務所創(chuàng)建的一個Layer對象的內部的一個SurfaceLayer對象。
獲得了一個實現(xiàn)了ISurface接口的Binder代理對象binder之后,C++層的Surface類的成員函數(shù)readFromParcel就可以將它封裝在一個C++層的Surface對象中了,并且將這個C++層的Surface對象返回給調用者。注意,C++層的Surface類的成員函數(shù)readFromParcel在創(chuàng)建為Binder代理對象binder創(chuàng)建一個C++層的Surface對象之前,首先會在C++層的Surface類的靜態(tài)成員變量sCachedSurfaces所描述的一個DefaultKeyedVectort向量中檢查是否已經為存在一個對應的C++層的Surface對象了。如果已經存在,那么就會直接將這個C++層的Surface對象返回給調用者。
回到函數(shù)Surface_readFromParcel中,接下來它就會調用另外一個函數(shù)setSurface來將前面所獲得的一個C++層的Surface對象sur保存在參數(shù)clazz所描述的一個Java層的Surface對象的成員變量mNativeSurface中,如下所示:
static void setSurface(JNIEnv* env, jobject clazz, const sp<Surface>& surface) { Surface* const p = (Surface*)env->GetIntField(clazz, so.surface); if (surface.get()) { surface->incStrong(clazz); } if (p) { p->decStrong(clazz); } env->SetIntField(clazz, so.surface, (int)surface.get()); }全局變量so是一個類型為so_t的結構體,在前面的Step 9中,我們已經分析過它的定義了,函數(shù)setSurface首先通過它的成員變量surface來將參數(shù)clazz所描述的一個Java層的Surface對象的成員變量mNativeSurface轉換成一個Surface對象p。如果這個Surface對象p存在,那么就需要調用它的成員函數(shù)decStrong來減少它的強引用計數(shù),因為接下來參數(shù)clazz所描述的一個Java層的Surface對象不再通過成員變量mNativeSurface來引用它了。
函數(shù)setSurface接下來就會將參數(shù)surface所指向的一個C++層的Surface對象的地址值保存在參數(shù)clazz所描述的一個Java層的Surface對象的成員變量mNativeSurface中。在執(zhí)行這個操作之前,函數(shù)setSurface需要調用參數(shù)surface所指向的一個C++層的Surface對象的成員函數(shù)incStrong來增加它的強引用計數(shù),這是因為接下來它要被參數(shù)clazz所描述的一個Java層的Surface對象通過成員變量mNativeSurface來引用了。
至此,我們就分析完成Android應用程序窗口的繪圖表面的創(chuàng)建過程了。通過這個過程我們就可以知道:
總結
以上是生活随笔為你收集整理的Surface的创建过程分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java计算两点坐标之间的距离
- 下一篇: .net的label的背景如何设置成为透