android window(三)lWindow添加流程


WindowManagerService只負責窗口管理,並不負責View的繪制跟圖層混合,本文就來分析WMS到底是怎么管理窗口的。初接觸Android時感覺:Activity似乎就是Google封裝好的窗口,APP只要合理的啟動新的Activity就打開了新窗口,這樣理解沒什么不對,Activity確實可以看做一種窗口及View的封裝,不過從源碼來看,Activity跟Window還是存在不同。本文主要從窗口的添加流程來將APP端、WMS端、SurfaceFlinger端三塊串聯起來,主要說一下幾個方面

  • 窗口的分類:Activity、Dialog、PopupWindow、Toast等對應窗口的區別
  • Window、IWindow 、WindowState、WindowToken、AppToken等之間的關系
  • 窗口的添加及Surface申請與Binder傳遞

窗口的分類簡述

在Android系統中,PopupWindow、Dialog、Activity、Toast等都有窗口的概念,但又各有不同,Android將窗口大致分為三類:應用窗口、子窗口、系統窗口。其中,Activity與Dialog屬於應用窗口、PopupWindow屬於子窗口,必須依附到其他非子窗口才能存在,而Toast屬於系統窗口,Dialog可能比較特殊,從表現上來說偏向於子窗口,必須依附Activity才能存在,但是從性質上來說,仍然是應用窗口,有自己的WindowToken,不同窗口之間的關系后面會更加詳細的分析,這里有一個概念即可。


窗口的添加

Activity並不是View展示的唯一方式,分析窗口添加流程的話,Activity也並不是最好的例子,因為Activity還會牽扯到AMS的知識,這里我們不用Activity,而是用一個懸浮View的展示來分析窗口的添加,代碼入下:

private void addTextViewWindow(Context context){
    TextView mview=new TextView(context);
    ...//設置顏色 樣式
    //關鍵點1
    WindowManager mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
    //關鍵點2
    wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
    wmParams.format = PixelFormat.RGBA_8888;
    wmParams.width = 800;
    wmParams.height = 800;
    //關鍵點3
    mWindowManager.addView(mview, wmParams);
}
這有三點比較關鍵,關鍵點1:獲取WindowManagerService服務的代理對象,不過對於Application而言,獲取到的其實是一個封裝過的代理對象,一個WindowManagerImpl實例,Application 的getSystemService()源碼其實是在ContextImpl中:有興趣的可以看看APP啟動時Context的創建:

 @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

SystemServiceRegistry類用靜態字段及方法中封裝了一些服務的代理,其中就包括WindowManagerService

    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }
    
    static {
             ...
             registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx.getDisplay());
            }});
            ...
    }

因此context.getApplicationContext().getSystemService()最終可以簡化為new WindowManagerImpl(ctx.getDisplay()),下面看下WindowManagerImpl的構造方法,它有兩個實現方法,對於Activity跟Application其實是有區別的,這點后面分析:

http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/WindowManagerImpl.java

public WindowManagerImpl(Display display) {
    this(display, null);
}

private WindowManagerImpl(Display display, Window parentWindow) {
    mDisplay = display;
    mParentWindow = parentWindow;
}
對於Application采用的是一參構造方法,所以其mParentWindow=null,這點后面會有用,到這里,通過getService獲取WMS代理的封裝類,接着看第二點,WindowManager.LayoutParams,主要看一個type參數,這個參數決定了窗口的類型,這里我們定義成 一個Toast窗口,屬於系統窗口,不需要處理父窗口、子窗口之類的事,更容易分析,最后看關鍵點3,利用WindowManagerImpl的addView方法添加View到WMS,
 @Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

不過很明顯WindowManagerImpl最后是委托mGlobal來進行這項操作,WindowManagerGlobal是一個單利,一個進程只有一個:

接着看WindowManagerGlobal的addView,對於添加系統窗口,這里將將代碼精簡一下,不關系子窗口等之類的邏輯
 public void addView(View view, ViewGroup.LayoutParams params,
         //...

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
           //...
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
           //...
        }
    }
先看關鍵點1,在向WMS添加View的時候,WindowManagerGlobal首先為View新建了一個ViewRootImpl,ViewRootImpl可以看做也是Window和View之間的通信的紐帶,比如將View添加到WMS、處理WMS傳入的觸摸事件、通知WMS更新窗口大小等、同時ViewRootImpl也封裝了View的繪制與更新方法等。看一下ViewRootImpl如何通過setView將視圖添加到WMS的:
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                  ...
                    //關鍵點1 
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    //關鍵點2
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
               ...
            }
}
先看關鍵點1,這里是先為relayout占一個位置,其實是依靠Handler先發送一個Message,排在所有WMS發送過來的消息之前,先布局繪制一次,之后才會處理WMS傳來的各種事件,比如觸摸事件等,畢竟要首先將各個View的布局、位置處理好,才能准確的處理WMS傳來的事件。
接着看做關鍵點2,這里才是真正添加窗口的地方,雖然關鍵點1執行在前,但是用的是Handler發消息的方式來處理,其Runable一定是在關鍵點2之后執行,接着看關鍵點2,這里有個比較重要的對象mWindowSession與mWindow,兩者都是在ViewRootImpl在實例化的時候創建的:
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
    mContext = context;
    mWindowSession = WindowManagerGlobal.getWindowSession();
    mWindow = new W(this);
}
mWindowSession它是通過WindowManagerGlobal.getWindowSession獲得的一個Binder服務代理,是App端向WMS發送消息的通道。
相對的,mWindow是一個 W extends IWindow.Stub Binder服務對象,
    static class W extends IWindow.Stub {
        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;

        W(ViewRootImpl viewAncestor) {
            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
            mWindowSession = viewAncestor.mWindowSession;
        }

        @Override
        public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                Configuration newConfig) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                        visibleInsets, stableInsets, outsets, reportDraw, newConfig);
            }
        }

        @Override
        public void moved(int newX, int newY) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchMoved(newX, newY);
            }
        }

        @Override
        public void dispatchAppVisibility(boolean visible) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchAppVisibility(visible);
            }
        }

        @Override
        public void dispatchGetNewSurface() {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchGetNewSurface();
            }
        }

        @Override
        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
            }
        }

        private static int checkCallingPermission(String permission) {
            try {
                return ActivityManagerNative.getDefault().checkPermission(
                        permission, Binder.getCallingPid(), Binder.getCallingUid());
            } catch (RemoteException e) {
                return PackageManager.PERMISSION_DENIED;
            }
        }

        @Override
        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                final View view = viewAncestor.mView;
                if (view != null) {
                    if (checkCallingPermission(Manifest.permission.DUMP) !=
                            PackageManager.PERMISSION_GRANTED) {
                        throw new SecurityException("Insufficient permissions to invoke"
                                + " executeCommand() from pid=" + Binder.getCallingPid()
                                + ", uid=" + Binder.getCallingUid());
                    }

                    OutputStream clientStream = null;
                    try {
                        clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out);
                        ViewDebug.dispatchCommand(view, command, parameters, clientStream);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (clientStream != null) {
                            try {
                                clientStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }

        @Override
        public void closeSystemDialogs(String reason) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchCloseSystemDialogs(reason);
            }
        }

        @Override
        public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
                boolean sync) {
            if (sync) {
                try {
                    mWindowSession.wallpaperOffsetsComplete(asBinder());
                } catch (RemoteException e) {
                }
            }
        }

        @Override
        public void dispatchWallpaperCommand(String action, int x, int y,
                int z, Bundle extras, boolean sync) {
            if (sync) {
                try {
                    mWindowSession.wallpaperCommandComplete(asBinder(), null);
                } catch (RemoteException e) {
                }
            }
        }

        /* Drag/drop */
        @Override
        public void dispatchDragEvent(DragEvent event) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchDragEvent(event);
            }
        }

        @Override
        public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
                int localValue, int localChanges) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility,
                        localValue, localChanges);
            }
        }

        @Override
        public void onAnimationStarted(int remainingFrameCount) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchWindowAnimationStarted(remainingFrameCount);
            }
        }

        @Override
        public void onAnimationStopped() {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchWindowAnimationStopped();
            }
        }

        @Override
        public void dispatchWindowShown() {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchWindowShown();
            }
        }
    }
View Code
其實可以看做是App端的窗口對象,主要作用是傳遞給WMS,並作為WMS向APP端發送消息的通道,在Android系統中存在大量的這種互為C\S的場景。接着看mWindowSession獲取的具體操首先通過getWindowManagerService 獲取WMS的代理,之后通過WMS的代理在服務端open一個Session,並在APP端獲取該Session的代理:
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                     //關鍵點1
                    IWindowManager windowManager = getWindowManagerService();
            //關鍵點2
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to open window session", e);
                }
            }
            return sWindowSession;
        }
    }
看關鍵點1 :首先要記住sWindowSession是一個單例的對象,之后就可以將getWindowManagerService函數其實可以簡化成下面一句代碼,其實就是獲得WindowManagerService的代理,之前的WindowManagerImpl都是一個殼子,或者說接口封裝,並未真正的獲得WMS的代理:
  public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try {
                    sWindowManagerService = getWindowManagerService();
                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
                }
            }
            return sWindowManagerService;
        }
    }
再看關鍵點2:sWindowSession = windowManager.openSession,它通過binder驅動后,會通知WMS回調openSession,打開一個Session返回給APP端,而 Session extends IWindowSession.Stub ,很明顯也是一個Binder通信的Stub端,封裝了每一個Session會話的操作。
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
        IInputContext inputContext) {
    if (client == null) throw new IllegalArgumentException("null client");
    if (inputContext == null) throw new IllegalArgumentException("null inputContext");
    Session session = new Session(this, callback, client, inputContext);
    return session;
}

到這里看到如何獲取Session,下面就是利用Session來add一個窗口:其實是調用Session.java的addToDisplayWithoutInputChannel函數

http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/Session.java

final class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
      //...
        @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
               outContentInsets, outStableInsets, outOutsets, outInputChannel);
  }
}

 

在WindowManager的LayoutParams中,與type同等重要的還有token。

上面說到:在源碼中token一般代表的是Binder對象,作用於IPC進程間數據通訊。並且它也包含着此次通訊所需要的信息,在ViewRootImpl里,token用來表示mWindow(W類,即IWindow),並且在WmS中只有符合要求的token才能讓Window正常顯示。

如此一來,Window的添加請求就交給WmS去處理了,在WmS內部會為每一個應用保留一個單獨的Session。在WmS 端會創建一個WindowState對象用來表示當前添加的窗口。 WmS負責管理這里些 WindowState 對象。至此,Window的添加過程就結束了。

至於Window的刪除和更新過程,舉一反三,也是使用WindowManagerGlobal對ViewRootImpl的操作,最終也是通過Session的IPC跨進程通信通知到WmS。整個過程的本質都是同出一轍的。

在WMS中addWindow又做了什么呢,就像名字寫的,負責添加一個窗口,代碼精簡后如下:

http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        ...
        synchronized(mWindowMap) {
        ...
        //關鍵點1 不能重復添加
            if (mWindowMap.containsKey(client.asBinder())) {
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
        //關鍵點2 對於子窗口類型的處理 1、必須有父窗口 2,父窗口不能是子窗口類型
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                parentWindow = windowForClientLocked(null, attrs.token, false);
                if (parentWindow == null) {
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }}
           ...
           boolean addToken = false;
            //關鍵點3 根據IWindow 獲取WindowToken WindowToken是窗口分組的基礎,每個窗口必定有一個分組-
            WindowToken token = mTokenMap.get(attrs.token);
          //關鍵點4對於Toast類系統窗口,其attrs.token可以看做是null, 如果目前沒有其他的類似系統窗口展示,token仍然獲取不到,仍然要走新建流程
            if (token == null) {
            ...    
                token = new WindowToken(this, attrs.token, -1, false);
                addToken = true;
            } 
            ...
             //關鍵點5 新建WindowState,WindowState與窗口是一對一的關系,可以看做是WMS中與窗口的抽象實體
            WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            ...
            if (addToken) {
                mTokenMap.put(attrs.token, token);
            }
            win.attach();
            mWindowMap.put(client.asBinder(), win);
            ... 
           //關鍵點6
          addWindowToListInOrderLocked(win, true);
        return res;
    }

 

這里有幾個概念需要先了解下:

  • IWindow:APP端窗口暴露給WMS的抽象實例,在ViewRootImpl中實例化,與ViewRootImpl一一對應,同時也是WMS向APP端發送消息的Binder通道。
  • WindowState:WMS端窗口的令牌,與IWindow,或者說與窗口一一對應,是WMS管理窗口的重要依據。
  • WindowToken:窗口的令牌,其實也可以看做窗口分組的依據,在WMS端,與分組對應的數據結構是WindowToken(窗口令牌),而與組內每個窗口對應的是WindowState對象,每塊令牌(AppWindowToken、WindowToken)都對應一組窗口(WindowState),Activity與Dialog對應的是AppWindowToken,PopupWindow對應的是普通的WindowToken。
  • AppToken:其實是ActivityRecord里面的IApplicationToken.Stub appToken 代理,也是ActivityClientRecord里面的token,可以看做Activity在其他服務(非AMS)的抽象

那么接着

關鍵點1:一個窗口不能被添加兩次,IWindow是一個Binder代理,在WMS端,一個窗口只會有一個IWindow代理,這是由Binder通信機制保證的,這個對象不能被添加兩次,否則會報錯。

關鍵點2,如果是子窗口的話,父窗口必須已被添加,由於我們分析的是系統Toast窗口,可以先不用關心;

關鍵點3,WindowManager.LayoutParams中有一個token字段,該字段標志着窗口的分組屬性,比如Activity及其中的Dialog是復用用一個AppToken,Activity里的PopupWindow復用一個IWindow類型Token,其實就是Activity的ViewRootImpl里面創建的IWindow,而對於我們現在添加的Toast類系統窗口,並未設置其attrs.token,那即是null,其實所有的Toast類系統窗口的attrs.token都可以看做null,就算不是null,也會在WMS被強制設置為null。所以Toast類系統窗口必定復用一個WindowToken,也可以說所有的Toast類系統窗口都是位於同一分組,這也是因為該類型系統窗口太常用,而且為所有進程服務,直接用一個WindowToken管理更加快捷,畢竟快速新建與釋放WindowToken也算是一種開銷。假設到我們添加系統窗口的時候,沒有任何系統窗口展示,是獲取不到key=null的WindowToken的,要新建WindowToken,並且添加到全局的TokenMap中,

而關鍵點5,其實就是新建窗口在WMS端的抽象實例:WindowState,它同窗口一一對應,詳細記錄了窗口的參數、Z順序、狀態等各種信息,新建只有會被放入全局的Map中,同時也會被附加到相應的WindowToken分組中去,到這里APP端向WMS注冊窗口的流程就算走完了,不過只算完成了前半部分,WMS還需要向SurfaceFlinger申請Surface,才算完成真正的分配了窗口。在向SurfaceFlinger申請Surface之前,WMS端需要獲得SF的代理,在WindowState對象創建后會利用 win.attach()函數為當前APP申請建立SurfaceFlinger的鏈接:

 
               
void attach() {
    if (WindowManagerService.localLOGV) Slog.v(
    mSession.windowAddedLocked();
}

void windowAddedLocked() {
    if (mSurfaceSession == null) {
       // SurfaceSession新建
        mSurfaceSession = new SurfaceSession();
        mService.mSessions.add(this);
       ...
    }
    mNumWindow++;
}

可以看到SurfaceSession對於Session來說是單利的,也就是與APP的Seesion一一對應,SurfaceSession所握着的SurfaceFlinger的代理其實就是SurfaceComposerClient,其實現如下:

    public SurfaceSession() {
        mNativeClient = nativeCreate();
    }

    static jlong nativeCreate(JNIEnv* env, jclass clazz) {
        SurfaceComposerClient* client = new SurfaceComposerClient();
        client->incStrong((void*)nativeCreate);
        return reinterpret_cast<jlong>(client);
    }
ession與APP進程是一一對應的,它會進一步為當前進程建立SurfaceSession會話,可以這么理解:Session是APP同WMS通信的通道,SurfaceSession是WMS為APP向SurfaceFlinger申請的通信通道,同樣 SurfaceSession與APP也是一一對應的,既然是同SurfaceFlinger通信的信使,那么SurfaceSession就應該握着SurfaceFlinger的代理,其實就是SurfaceComposerClient里的ISurfaceComposerClient mClient對象,它是SurfaceFlinger為每個APP封裝一個代理,也就是 **進程 <-> Session <-> SurfaceSession <-> SurfaceComposerClient <-> ISurfaceComposerClient(BpSurfaceComposerClient) **五者是一條線, 為什么不直接與SurfaceFlinger通信呢?大概為了SurfaceFlinger管理每個APP的Surface比較方便吧,這四個類的模型如下圖:
至於ISurfaceComposerClient(BpSurfaceComposerClient) 究竟是怎么樣一步步創建的,其實它是利用ComposerService這樣一個單利對象為為每個APP在WMS端申請一個ISurfaceComposerClient對象,在WMS端表現為BpSurfaceComposerClient,在SurfaceFlinger端表現為BnSurfaceComposerClient,具體代碼如下:

SurfaceComposerClient::SurfaceComposerClient()
    : mStatus(NO_INIT), mComposer(Composer::getInstance())
{
}
// 單利的,所以只有第一次的時候采用
void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
    if (sm != 0) {
        sp<ISurfaceComposerClient> conn = sm->createConnection();
        if (conn != 0) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
    sp<ISurfaceComposerClient> bclient;
    sp<Client> client(new Client(this));
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        bclient = client;
    }
    return bclient;
}

剛才說完成了前半部分,主要針對WMS的窗口管理,后半部分則是圍繞Surface的分配來進行的,還記得之前ViewRootImpl在setView時候分了兩步嗎?雖然先調用requestLayout先執行,但是由於其內部利用Handler發送消息延遲執行的,所以可以看做requestLayout是在addWindow之后執行的,那么這里就看添加窗口之后,如何分配Surface的,requestLayout函數調用里面使用了Hanlder的一個小手段,那就是利用postSyncBarrier添加了一個Barrier(擋板),這個擋板的作用是阻塞普通的同步消息的執行,在擋板被撤銷之前,只會執行異步消息,而requestLayout先添加了一個擋板Barrier,之后自己插入了一個異步任務mTraversalRunnable,其主要作用就是保證mTraversalRunnable在所有同步Message之前被執行,保證View繪制的最高優先級。具體實現如下:

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;

            //關鍵點1 添加塞子
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //關鍵點2 添加異步消息任務
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...
mTraversalRunnable任務的主要作用是:如果Surface未分配,則請求分配Surface,並測量、布局、繪圖,其執行主體其實是performTraversals()函數,該函數包含了APP端View繪制大部分的邏輯, performTraversals函數很長,這里只簡要看幾個點,其實主要是關鍵點1:relayoutWindow:


private void performTraversals() {
            final View host = mView;
             ...
    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null) {
            //關鍵點1 申請Surface或者重新設置參數
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
          //關鍵點2 測量
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            }        
          //關鍵點3 布局
                performLayout(lp, desiredWindowWidth, desiredWindowHeight);
           //關鍵點4 更新window
              try {
                mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
                        contentInsets, visibleInsets, touchableRegion);
            ...
          //關鍵點5 繪制
           performDraw();
           ...  
       }
relayoutWindow主要是通過mWindowSession.relayout向WMS申請或者更新Surface如下,這里只關心一個重要的參數mSurface,在Binder通信中mSurface是一個out類型的參數,也就是Surface內部的內容需要WMS端負責填充,並回傳給APP端:

   private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
       ...
        int relayoutResult = mWindowSession.relayout(
                mWindow, mSeq, params, ...  mSurface);
        ...
        return relayoutResult;
    }
看下到底relayout是如何想SurfaceFlinger申請Surface的。我們知道每個窗口都有一個WindowState與其對應,另外每個窗口也有自己的動畫,比如入場/出廠動畫,而WindowStateAnimator就是與WindowState的動畫,為什么要提WindowStateAnimator,因為WindowStateAnimator是
public int relayoutWindow(Session session, IWindow client, int seq,... Surface outSurface) {
         WindowState win = windowForClientLocked(session, client, false);
        WindowStateAnimator winAnimator = win.mWinAnimator;
         //關鍵點1 
           SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
           if (surfaceControl != null) {
         //關鍵點2
             outSurface.copyFrom(surfaceControl);
                } else {
                    outSurface.release();
                }
這里只看Surface創建代碼,首先通過windowForClientLocked找到WindowState,利用WindowState的WindowStateAnimator成員創建一個SurfaceControl,SurfaceControl會調用native函數nativeCreate(session, name, w, h, format, flags)創建Surface,


static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags) {
    ScopedUtfChars name(env, nameStr);
    //關鍵點1
    sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
    //關鍵點2
    sp<SurfaceControl> surface = client->createSurface(
            String8(name.c_str()), w, h, format, flags);
    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get());
}

關鍵點1是取到SurfaceSession對象中SurfaceComposerClient對象,之后調用SurfaceComposerClient的createSurface方法進一步創建SurfaceControl,

 

sp<SurfaceControl> SurfaceComposerClient::createSurface(
        const String8& name,
        uint32_t w,
        uint32_t h,
        PixelFormat format,
        uint32_t flags)
{
    sp<SurfaceControl> sur;
    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IGraphicBufferProducer> gbp;
        //關鍵點1 獲取圖層的關鍵信息handle, gbp
        status_t err = mClient->createSurface(name, w, h, format, flags,
                &handle, &gbp);
        //關鍵點2 根據返回的圖層關鍵信息 創建SurfaceControl對象
        if (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);
        }
    }
    return sur;
}
這里先看mClient->createSurface,SurfaceComposerClient的mClient其實是一個BpSurfaceComposerClient對象,它SurfaceFlinger端Client在WMS端的代理,因此創建Surface的代碼還是在SurfaceFlinger服務端的Client對象中,這里有兩個關鍵的變量sp<IBinder> handle與 sp<IGraphicBufferProducer> gbp,前者標志在SurfaceFlinger端的圖層,后者用來創建GraphicBuffer,兩者類型都是IBinder類型,同時也是需要SurfaceFlinger填充的對象,這兩者是一個圖層對應的最關鍵的信息:
status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp){
    ...
    //關鍵點2 這里並未直接創建 ,而是通過發送了一個MessageCreateLayer消息
    sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle, gbp);
    mFlinger->postMessageSync(msg);
    return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}

Client 並不會直接新建圖層,而是向SurfaceFlinger發送一個MessageCreateLayer消息,通知SurfaceFlinger服務去執行,其handler代碼如下:

 

class MessageCreateLayer : public MessageBase {
        SurfaceFlinger* flinger;
        Client* client;
            virtual bool handler() {
            result = flinger->createLayer(name, client, w, h, format, flags,
                    handle, gbp);
            return true;
        }
    };

其實就是調用SurfaceFlinger的createLayer,創建一個圖層,到這里才是真正的創建圖層:

status_t SurfaceFlinger::createLayer(
        const String8& name,
        const sp<Client>& client,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)
{
    if (int32_t(w|h) < 0) {
        return BAD_VALUE;
    }

    status_t result = NO_ERROR;

    sp<Layer> layer;
     //關鍵點1 新建不同圖層
    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceNormal:
            result = createNormalLayer(client,
                    name, w, h, flags, format,
                    handle, gbp, &layer);
            break;
        case ISurfaceComposerClient::eFXSurfaceDim:
            result = createDimLayer(client,
                    name, w, h, flags,
                    handle, gbp, &layer);
            break;
        default:
            result = BAD_VALUE;
            break;
    }

    if (result != NO_ERROR) {
        return result;
    }
   ...
}

SurfaceFlinger會根據不同的窗口參數,創建不同類型的圖層,這里只看一下createNormalLayer普通樣式的圖層,

 

status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
        const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
    // initialize the surfaces
    switch (format) {
    case PIXEL_FORMAT_TRANSPARENT:
    case PIXEL_FORMAT_TRANSLUCENT:
        format = PIXEL_FORMAT_RGBA_8888;
        break;
    case PIXEL_FORMAT_OPAQUE:
        format = PIXEL_FORMAT_RGBX_8888;
        break;
    }
    //關鍵點 1
    *outLayer = new Layer(this, client, name, w, h, flags);
    status_t err = (*outLayer)->setBuffers(w, h, format, flags);
    //關鍵點 2
    if (err == NO_ERROR) {
        *handle = (*outLayer)->getHandle();
        *gbp = (*outLayer)->getProducer();
    }
  return err;
}

可以看到 圖層最終對應的是Layer,這里會新建一個Layer對象,Layer中包含着與這個圖層對應的Handle及Producer對象,Handle可以看做是Surface的唯一性標識,不過好像沒太大的作用,最多是一個標識,將來清理的時候有用。相比之下gbp = (*outLayer)->getProducer()比較重要,它實際是一個BufferQueueProducer對象,關系到共享內存的分配問題,后面會專門分析,這里到此打住,我們終於得到了一個圖層對象,到這里之后,我們梳理一下,圖層如何建立的:

  • 首先APP端新建一個Surface圖層的容器殼子,
  • APP通過Binder通信將這個Surface的殼子傳遞給WMS,
  • WMS為了填充Surface去向SurfaceFlinger申請真正的圖層,
  • SurfaceFlinger收到WMS請求為APP端的Surface分配真正圖層
  • 將圖層相關的關鍵信息Handle及Producer傳遞給WMS

Layer建立之后,SurfaceFlinger會將圖層標識信息Handle及Producer傳遞給WMS,WMS利用這兩者創建一個SurfaceControl對象,之后再利用該對象創建Surface,具體代碼如下:

 
                      

void getSurface(Surface outSurface) {
    outSurface.copyFrom(mSurfaceControl);
}

public void copyFrom(SurfaceControl other) {
long surfaceControlPtr = other.mNativeObject;
long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
synchronized (mLock) {
    setNativeObjectLocked(newNativeObject);
}
}

可以看到Surface的拷貝函數其實就是直接修改Surface native對象指針值,native的Surface對象中包含mGraphicBufferProducer對象,很重要,會被傳遞給APP端。

static jlong nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz,
        jlong surfaceControlNativeObj) {

    sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
    sp<Surface> surface(ctrl->getSurface());
    if (surface != NULL) {
        surface->incStrong(&sRefBaseOwner);
    }
    return reinterpret_cast<jlong>(surface.get());
}

sp<Surface> SurfaceControl::getSurface() const
{
    Mutex::Autolock _l(mLock);
    if (mSurfaceData == 0) {
        mSurfaceData = new Surface(mGraphicBufferProducer, false);
    }
    return mSurfaceData;
}

到這里WMS端Surface創建及填充完畢,並且Surface其實與WMS的SurfaceControl一一對應,當APP端需要在圖層級別進行操控的時候,其實還是要依靠SurfaceControl的,WMS的Surface創建完畢后,需要傳遞給APP端,之后APP端就獲得直接同SurfaceFlinger通信的能力,比如繪圖與UI更新,怎傳遞的呢?我們知道Surface實現了Parcel接口,因此可以傳遞序列化的數據,其實看一下Surface nativeReadFromParcel就知道到底是怎么傳遞的了,利用readStrongBinder獲取IGraphicBufferProducer對象的句柄,之后轉化為IGraphicBufferProducer代理其實就是BpGraphicBufferProducer,之后利用BpGraphicBufferProducer構建Surface,這樣APP端Surface就被填充完畢,可以同SurfaceFlinger通信了:

 
                       
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject parcelObj) {
    Parcel* parcel = parcelForJavaObject(env, parcelObj);
    if (parcel == NULL) {
        doThrowNPE(env);
        return 0;
    }
     sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
    sp<IBinder> binder(parcel->readStrongBinder());
    if (self != NULL
            && (IInterface::asBinder(self->getIGraphicBufferProducer()) == binder)) {
        return jlong(self.get());
    }
    sp<Surface> sur;
    sp<IGraphicBufferProducer> gbp(interface_cast<IGraphicBufferProducer>(binder));
    if (gbp != NULL) {
        sur = new Surface(gbp, true);
        sur->incStrong(&sRefBaseOwner);
    }

    if (self != NULL) {
        self->decStrong(&sRefBaseOwner);
    }

    return jlong(sur.get());
}

到這里為止,APP<->WMS <->WMS 通信申請Surface的流程算走完了

總結

窗口的添加流程簡化如下,這里暫且忽略窗口的分組管理。

  • APP首先去WMS登記窗口
  • WMS端登記窗口
  • APP新建Surface殼子,請求WMS填充Surface
  • WMS請求SurfaceFlinger分配窗口圖層
  • SurfaceFlinger分配Layer,將結果回傳給WMS
  • WMS將窗口信息填充到Surface傳輸到APP
  • APP端獲得填充信息,獲取與SurfaceFlinger通信的能力

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM