出自:http://blog.csdn.net/luoshengyang/article/details/8303098
在前文中,我們分析了應用程序窗口連接到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();
- }
- ......
- }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。
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);
- }
- }
- ......
- }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。
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();
- }
- }
- ......
- }
這個函數定義在文件frameworks/base/core/java/android/view/ViewRoot.java中。
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);
- ......
- }
這個接口定義在frameworks/base/core/java/android/view/IWindowSession.aidl文件中。
使用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;
- }
- ......
- }
- ......
- }
- ......
- }
這個函數定義在文件out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowSession.java中。
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;
- }
- ......
- }
- ......
- }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
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);
- }
- ......
- }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
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 (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;
- }
- ......
- }
- ......
- }
這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
在創建一個應用程序窗口的繪圖表面之前,我們需要知道以下數據:
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類的構造函數的實現,以便可以了解一個應用程序窗口的繪圖表面的詳細創建過程。
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;
- ......
- }
這個函數定義在文件frameworks/base/core/java/android/view/Surface.java中。
Surface類有三個成員變量mSurfaceControl、mCanvas和mName,它們的類型分別是int、Canvas和mName,其中,mSurfaceControl保存的是在C++層的一個SurfaceControl對象的地址值,mCanvas用來描述一塊類型為CompatibleCanvas的畫布,mName用來描述當前正在創建的一個繪圖表面的名稱。畫布是真正用來繪制UI的地方,不過由於現在正在創建的繪圖表面是在WindowManagerService服務這一側使用的,而WindowManagerService服務不會去繪制應用程序窗口的UI,它只會去設置應用程序窗口的屬性,因此,這里創建的畫布實際上沒有什么作用,我們主要關注與成員變量mSurfaceControl所關聯的C++層的SurfaceControl對象是如何創建的。
Surface類的構造函數是通過調用另外一個成員函數init來創建與成員變量mSurfaceControl所關聯的C++層的SurfaceControl對象的。Surface類的成員函數init是一個JNI方法,它是由C++層的函數Surface_init來實現的,如下所示:
- 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);
- }
這個函數定義在文件frameworks/base/core/jni/android_view_Surface.cpp中。
參數session指向了在Java層所創建的一個SurfaceSession對象。從前面Android應用程序窗口(Activity)與WindowManagerService服務的連接過程分析一文可以知道,Java層的SurfaceSession對象有一個成員變量mClient,它指向了在C++層中的一個SurfaceComposerClient對象。從前面Android應用程序與SurfaceFlinger服務的關系概述和學習計划這一系列的文章又可以知道,C++層的SurfaceComposerClient對象可以用來請求SurfaceFlinger服務為應用程序窗口創建繪圖表面,即創建一個Layer對象。
因此,函數首先將參數session所指向的一個Java層的SurfaceSession對象的成員變量mClient轉換成一個SurfaceComposerClient對象,然后再調用這個SurfaceComposerClient對象的成員函數createSurface來請求SurfaceFlinger服務來為參數clazz所描述的一個Java層的Surface對象所關聯的應用程序窗口創建一個Layer對象。SurfaceFlinger服務創建完成這個Layer對象之后,就會將該Layer對象內部的一個實現了ISurface接口的SurfaceLayer對象返回給函數,於是,函數就可以獲得一個實現了ISurface接口的Binder代理對象。這個實現了ISurface接口的Binder代理對象被封裝在C++層的一個SurfaceControl對象surface中。
注意,sso是一個全局變量,它是一個類型為sso_t的結構體,它的定義如下所示:
- struct sso_t {
- jfieldID client;
- };
- static sso_t sso;
它的成員函數client用來描述Java層中的SurfaceSession類的成員變量mClient在類中的偏移量,因此,函數Surface_init通過這個偏移量就可以訪問參數session所指向的一個SurfaceSession對象的成員變量mClient的值,從而獲得一個SurfaceComposerClient對象。
另外一個需要注意的地方是,當參數name的值等於null時,函數Surface_init調用前面所獲得一個SurfaceComposerClient對象的六個參數版本的成員函數createSurface來請求SurfaceFlinger服務創建一個Layer對象,否則的話,就調用七個參數版本的成員函數createSurface來請求SurfaceFlinger服務創建一個Layer對象。SurfaceComposerClient類的成員函數createSurface的實現可以參考前面Android應用程序請求SurfaceFlinger服務創建Surface的過程分析一文。
得到了SurfaceControl對象surface之后,函數Surface_init接下來繼續調用另外一個函數setSurfaceControl來它的地址值保存在參數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());
- }
這個函數定義在文件frameworks/base/core/jni/android_view_Surface.cpp中。
在分析函數setSurfaceControl的實現之前,我們先分析全局變量so的定義,如下所示:
- 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在類中的偏移量。
回到函數setSurfaceControl中,它首先通過結構體so的成員變量surfaceControl來獲得參數clazz所指向的一個Java層的Surface對象的成員變量mSurfaceControl所關聯的一個C++層的SurfaceControl對象。如果這個SurfaceControl對象存在,那么變量p的值就不會等於null,在這種情況下,就需要調用它的成員函數decStrong來減少它的強引用計數,因為接下來參數clazz所指向的一個Java層的Surface對象不再通過成員變量mSurfaceControl來引用它了。
另一方面,函數setSurfaceControl需要增加參數surface所指向的一個C++層的SurfaceControl對象的強引用計數,即調用參數surface所指向的一個C++層的SurfaceControl對象的成員函數incStrong,因為接下來參數clazz所指向的一個Java層的Surface對象要通過成員變量mSurfaceControl來引用它,即將它的地址值保存在參數clazz所指向的一個Java層的Surface對象的成員變量mSurfaceControl中。
這一步執行完成之后,沿着調用路徑層層返回,回到前面的Step 8中,即WindowState類的成員函數createSurfaceLocked中,這時候一個繪圖表面就創建完成了。這個繪圖表面最終會返回給請求創建它的應用程序進程,即前面的Step 5,也就是IWindowSession.Stub.Proxy類的成員函數relayout。
IWindowSession.Stub.Proxy類的成員函數relayout獲得了從WindowManagerService服務返回來的一個繪圖表面,即一個Java層的Surface對象之后,就會將它的內容拷貝到參數outSurface所描述的另外一個Java層的Surface對象中,這里通過調用Surface類的成員函數readFromParcel來實現的。注意,參數outSurface所描述的這個Java層的Surface對象是在應用程序進程這一側創建的,它的作用是用來繪制應用程序窗品的UI。
接下來,我們就繼續分析Surface類的成員函數readFromParcel的實現,以便可以了解在應用程序進程這一側的Surface對象是如何創建的。
Step 10. Surface.readFromParcel
- public class Surface implements Parcelable {
- ......
- public native void readFromParcel(Parcel source);
- ......
- }
這個函數定義在文件frameworks/base/core/java/android/view/Surface.java中。
Surface類的成員函數readFromParcel是一個JNI方法,它是由C++層的函數Surface_readFromParcel來實現的,如下所示:
- 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);
- }
這個函數定義在文件frameworks/base/core/jni/android_view_Surface.cpp中。
參數argParcel所指向的一個Parcel對象的當前位置保存的是一個Java層的Surface對象的內容,函數readFromParcel首先調用C++層的Surface類的成員函數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;
- }
這個函數定義在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
參數data所指向的一個Parcel對象的當前位置保存的是一個Binder代理對象,這個Binder代理對象實現了ISurface接口,它所引用的Binder本地對象就是在前面的Step 9中WindowManagerService服務請求SurfaceFlinger服務所創建的一個Layer對象的內部的一個SurfaceLayer對象。
獲得了一個實現了ISurface接口的Binder代理對象binder之后,C++層的Surface類的成員函數readFromParcel就可以將它封裝在一個C++層的Surface對象中了,並且將這個C++層的Surface對象返回給調用者。注意,C++層的Surface類的成員函數readFromParcel在創建為Binder代理對象binder創建一個C++層的Surface對象之前,首先會在C++層的Surface類的靜態成員變量sCachedSurfaces所描述的一個DefaultKeyedVectort向量中檢查是否已經為存在一個對應的C++層的Surface對象了。如果已經存在,那么就會直接將這個C++層的Surface對象返回給調用者。
回到函數Surface_readFromParcel中,接下來它就會調用另外一個函數setSurface來將前面所獲得的一個C++層的Surface對象sur保存在參數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());
- }
這個函數定義在文件frameworks/base/core/jni/android_view_Surface.cpp中。
全局變量so是一個類型為so_t的結構體,在前面的Step 9中,我們已經分析過它的定義了,函數setSurface首先通過它的成員變量surface來將參數clazz所描述的一個Java層的Surface對象的成員變量mNativeSurface轉換成一個Surface對象p。如果這個Surface對象p存在,那么就需要調用它的成員函數decStrong來減少它的強引用計數,因為接下來參數clazz所描述的一個Java層的Surface對象不再通過成員變量mNativeSurface來引用它了。
函數setSurface接下來就會將參數surface所指向的一個C++層的Surface對象的地址值保存在參數clazz所描述的一個Java層的Surface對象的成員變量mNativeSurface中。在執行這個操作之前,函數setSurface需要調用參數surface所指向的一個C++層的Surface對象的成員函數incStrong來增加它的強引用計數,這是因為接下來它要被參數clazz所描述的一個Java層的Surface對象通過成員變量mNativeSurface來引用了。
至此,我們就分析完成Android應用程序窗口的繪圖表面的創建過程了。通過這個過程我們就可以知道:
1. 每一個應用程序窗口都對應有兩個Java層的Surface對象,其中一個是在WindowManagerService服務這一側創建的,而另外一個是在應用程序進程這一側創建的。
2. 在WindowManagerService服務這一側創建的Java層的Surface對象在C++層關聯有一個SurfaceControl對象,用來設置應用窗口的屬性,例如,大小和位置等。
3. 在應用程序進程這一側創建的ava層的Surface對象在C++層關聯有一個Surface對象,用來繪制應用程序窗品的UI。
理解上述三個結論對理解Android應用程序窗口的實現框架以及WindowManagerService服務的實現都非常重要。 一個應用程序窗口的繪圖表面在創建完成之后,接下來應用程序進程就可以在上面繪制它的UI了。在接下來的一篇文章中,我們就繼續分析Android應用程序窗品的繪制過程,敬請關注!
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!