266
技術社區[雲棲]
android之surfaceview畫圖
在前文中,我們分析了應用程序窗口連接到WindowManagerService服務的過程。在這個過程中,WindowManagerService服務會為應用程序窗口創建過一個到SurfaceFlinger服務的連接。有了這個連接之後,WindowManagerService服務就可以為應用程序窗口創建繪圖表麵了,以便可以用來渲染窗口的UI。在本文中,我們就詳細分析應用程序窗口的繪圖表麵的創建過程。從前麵Android應用程序與SurfaceFlinger服務的關係概述和學習計劃和Android係統Surface機製的SurfaceFlinger服務簡要介紹和學習計劃這兩個係列的文章可以知道,每一個在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對象,如圖1所示:
圖1 應用程序窗口的繪圖表麵的模型圖
在應用程序進程這一側,每一個應用程序窗口,即每一個Activity組件,都有一個關聯的Surface對象,這個Surface對象是保在在一個關聯的ViewRoot對象的成員變量mSurface中的,如圖2所示:
圖2 應用程序窗口在應用程序進程這一側的Surface的實現
圖2的類關係圖的詳細描述可以參考前麵Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文的圖6,這裏我們隻關注Surface類的實現。在應用程序進程這一側,每一個Java層的Surface對都對應有一個C++層的Surface對象,並且後者的地址值保存在前者的成員變量mNativeSurface中。C++層的Surface類的實現以及作用可以參考前麵Android應用程序與SurfaceFlinger服務的關係概述和學習計劃這個係列的文章。
在WindowManagerService服務這一側,每一個應用程序窗口,即每一個Activity組件,都有一個對應的WindowState對象,這個WindowState對象的成員變量mSurface同樣是指向了一個Surface對象,如圖3所示:
圖3 應用程序窗口在WindowManagerService服務這一側的Surface的實現
圖3的類關係圖的詳細描述可以參考前麵Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃一文的圖7,這裏我們同樣隻關注Surface類的實現。在WindowManagerService服務這一側,每一個Java層的Surface對都對應有一個C++層的SurfaceControl對象,並且後者的地址值保存在前者的成員變量mSurfaceControl中。C++層的SurfaceControl類的實現以及作用同樣可以參考前麵Android應用程序與SurfaceFlinger服務的關係概述和學習計劃這個係列的文章。
一個應用程序窗口分別位於應用程序進程和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)的創建過程分析一文可以知道,當一個應用程序窗口被激活並且它的視圖對象創建完成之後,應用程序進程就會調用與其所關聯的一個ViewRoot對象的成員函數requestLayout來請求對其UI進行布局以及顯示。由於這時候應用程序窗口的繪圖表麵尚未創建,因此,ViewRoot類的成員函數requestLayout就會請求WindowManagerService服務來創建繪圖表麵。接下來,我們就從ViewRoot類的成員函數requestLayout開始,分析應用程序窗口的繪圖表麵的創建過程,如圖4所示:
圖4 應用程序窗口的繪圖表麵的創建過程
這個過程可以分為10個步驟,接下來我們就詳細分析每一個步驟。
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類的成員函數requestLayout首先調用另外一個成員函數checkThread來檢查當前線程是否就是創建當前正在處理的ViewRoot對象的線程。如果不是的話,那麼ViewRoot類的成員函數checkThread就會拋出一個異常出來。ViewRoot類是從Handler類繼承下來的,用來處理應用程序窗口的UI布局和渲染等消息。由於這些消息都是與Ui相關的,因此它們就需要在UI線程中處理,這樣我們就可以推斷出當前正在處理的ViewRoot對象是要應用程序進程的UI線程中創建的。進一步地,我們就可以推斷出ViewRoot類的成員函數checkThread實際上就是用來檢查當前線程是否是應用程序進程的UI線程,如果不是的話,它就會拋出一個異常出來。
通過了上述檢查之後,ViewRoot類的成員函數requestLayout首先將其成員變量mLayoutRequested的值設置為true,表示應用程序進程的UI線程正在被請求執行一個UI布局操作,接著再調用另外一個成員函數scheduleTraversals來繼續執行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類的成員函數scheduleTraversals就什麼也不做,否則的話,它就會首先將成員變量mTraversalScheduled的值設置為true,然後再調用從父類Handler繼承下來的成員函數sendEmptyMessage來往應用程序進程的UI線程發送一個DO_TRAVERSAL消息。
這個類型為DO_TRAVERSAL的消息是由ViewRoot類的成員函數performTraversals來處理的,因此,接下來我們就繼續分析ViewRoot類的成員函數performTraversals的實現。
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類的成員函數performTraversals的實現是相當複雜的,這裏我們分析它的實現框架,在以後的文章中,我們再詳細分析它的實現細節。
在分析ViewRoot類的成員函數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。這個創建繪圖表麵的過程正是本文所要關心的。
理解了上述五個成員變量之後,我們就可以分析ViewRoot類的成員函數performTraversals的實現框架了,如下所示:
1. 將成員變量mView和mFullRedrawNeeded的值分別保存在本地變量host和fullRedrawNeeded中,並且將成員變量mTraversalScheduled的值設置為false,表示應用程序進程的UI線程中的DO_TRAVERSAL消息已經被處理了。
2. 本地變量newSurface用來描述當前正在處理的應用程序窗口在本輪的DO_TRAVERSAL消息處理中是否新創建了一個繪圖表麵,它的初始值為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線程在調用ViewRoot類的成員函數relayoutWindow來請求WindowManagerService服務重新布局係統中的所有窗口之前,會調用成員變量mSurface所指向的一個Surface對象的成員函數isValid來判斷它描述的是否是一個有效的繪圖表麵,並且將結果保存在本地變量hadSurface中。
6. 應用程序進程的UI線程在調用ViewRoot類的成員函數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進行重繪了,而是要等到下一個DO_TRAVERSAL消息到來的時候,再進行重繪,以便使得當前正在處理的應用程序窗口的各項參數可以得到重新設置。下一個DO_TRAVERSAL消息需要馬上被調度,因此,應用程序進程的UI線程就會重新執行ViewRoot類的成員函數scheduleTraversals。
這樣,我們就分析完成ViewRoot類的成員函數performTraversals的實現框架了,接下來我們就繼續分析ViewRoot類的成員函數relayoutWindow的實現,以便可以看到當前正在處理的應用程序窗口的繪圖表麵是如何創建的。
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;
- }
- ......
- }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。
ViewRoot類的成員函數relayoutWindow調用靜態成員變量sWindowSession所描述的一個實現了IWindowSession接口的Binder代理對象的成員函數relayout來請求WindowManagerService服務對成員變量mWindow所描述的一個應用程序窗口的UI進行重新布局,同時,還會將成員變量mSurface所描述的一個Surface對象傳遞給WindowManagerService服務,以便WindowManagerService服務可以根據需要來重新創建一個繪圖表麵給成員變量mWindow所描述的一個應用程序窗口使用。
實現了IWindowSession接口的Binder代理對象是由IWindowSession.Stub.Proxy類來描述的,接下來我們就繼續分析它的成員函數relayout的實現。
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類,它的成員函數relayout的實現如下所示:
- 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服務的一個連接的。
當運行在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的進程間通信請求的。
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類的成員函數relayout調用了外部類WindowManagerService的成員函數relayoutWindow來對參數參數window所描述的一個應用程序窗口的UI進行布局,接下來我們就繼續分析WindowManagerService類的成員函數relayoutWindow的實現。
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類的成員函數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類的成員變量mAppToken的具體描述可以參考前麵Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文。
注意,當WindowState對象win的成員變量mAppToken等於null時,隻要滿足條件1就可以了,因為這時候參數client所描述的窗口不是一個Activity組件窗口,它的可見性不像Activity組件窗口一樣受到Activity組件的可見性影響。
我們假設參數client所描述的是一個應用程序窗口,並且這個應用程序窗口是可見的,那麼WindowManagerService類的成員函數relayoutWindow接下來就會調用WindowState對象win的成員函數createSurfaceLocked來為它創建一個繪圖表麵。如果這個繪圖表麵能創建成功,那麼WindowManagerService類的成員函數relayoutWindow就會將它的內容拷貝到輸出參數outSource所描述的一個Surface對象去,以便可以將它返回給應用程序進程處理。另一方麵,如果這個繪圖表麵不能創建成功,那麼WindowManagerService類的成員函數relayoutWindow就會將輸出參數outSource所描述的一個Surface對象的內容釋放掉,以便應用程序進程知道該Surface對象所描述的繪圖表麵已經失效了。
接下來,我們就繼續分析WindowState類的成員函數createSurfaceLocked的實現,以便可以了解一個應用程序窗口的繪圖表麵的創建過程。
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最後更新:2017-04-03 12:53:58
上一篇:
AFDX總線協議規範
下一篇:
GNU  Radio 中OFDM  Tunnel 詳解