Android窗口管理分析(2):WindowManagerService窗口管理之Window添加流程

本文基于Android6.0橄唬。WindowManagerService只負(fù)責(zé)窗口管理,并不負(fù)責(zé)View的繪制跟圖層混合,本文就來(lái)分析WMS到底是怎么管理窗口的幢炸。初接觸Android時(shí)感覺(jué):Activity似乎就是Google封裝好的窗口充岛,APP只要合理的啟動(dòng)新的Activity就打開(kāi)了新窗口保檐,這樣理解沒(méi)什么不對(duì),Activity確實(shí)可以看做一種窗口及View的封裝崔梗,不過(guò)從源碼來(lái)看夜只,Activity跟Window還是存在不同。本文主要從窗口的添加流程來(lái)將APP端蒜魄、WMS端扔亥、SurfaceFlinger端三塊串聯(lián)起來(lái),主要說(shuō)一下幾個(gè)方面

  • 窗口的分類(lèi):Activity谈为、Dialog旅挤、PopupWindow、Toast等對(duì)應(yīng)窗口的區(qū)別
  • Window伞鲫、IWindow 粘茄、WindowState、WindowToken榔昔、AppToken等之間的關(guān)系
  • 窗口的添加及Surface申請(qǐng)與Binder傳遞

窗口的分類(lèi)簡(jiǎn)述

在Android系統(tǒng)中驹闰,PopupWindow、Dialog撒会、Activity嘹朗、Toast等都有窗口的概念,但又各有不同诵肛,Android將窗口大致分為三類(lèi):應(yīng)用窗口屹培、子窗口、系統(tǒng)窗口怔檩。其中褪秀,Activity與Dialog屬于應(yīng)用窗口、PopupWindow屬于子窗口薛训,必須依附到其他非子窗口才能存在媒吗,而Toast屬于系統(tǒng)窗口,Dialog可能比較特殊乙埃,從表現(xiàn)上來(lái)說(shuō)偏向于子窗口闸英,必須依附Activity才能存在锯岖,但是從性質(zhì)上來(lái)說(shuō),仍然是應(yīng)用窗口甫何,有自己的WindowToken出吹,不同窗口之間的關(guān)系后面會(huì)更加詳細(xì)的分析,這里有一個(gè)概念即可辙喂。

窗口組織形式.jpg

窗口的添加

Activity并不是View展示的唯一方式捶牢,分析窗口添加流程的話,Activity也并不是最好的例子巍耗,因?yàn)锳ctivity還會(huì)牽扯到AMS的知識(shí)秋麸,這里我們不用Activity,而是用一個(gè)懸浮View的展示來(lái)分析窗口的添加芍锦,代碼入下:

private void addTextViewWindow(Context context){

    TextView mview=new TextView(context);
    ...<!--設(shè)置顏色 樣式-->
    <!--關(guān)鍵點(diǎn)1-->
    WindowManager mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
    <!--關(guān)鍵點(diǎn)2-->
    wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
    wmParams.format = PixelFormat.RGBA_8888;
    wmParams.width = 800;
    wmParams.height = 800;
    <!--關(guān)鍵點(diǎn)3-->
    mWindowManager.addView(mview, wmParams);
}

這有三點(diǎn)比較關(guān)鍵竹勉,關(guān)鍵點(diǎn)1:獲取WindowManagerService服務(wù)的代理對(duì)象,不過(guò)對(duì)于Application而言娄琉,獲取到的其實(shí)是一個(gè)封裝過(guò)的代理對(duì)象次乓,一個(gè)WindowManagerImpl實(shí)例,Application 的getSystemService()源碼其實(shí)是在ContextImpl中:有興趣的可以看看APP啟動(dòng)時(shí)Context的創(chuàng)建:

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

SystemServiceRegistry類(lèi)用靜態(tài)字段及方法中封裝了一些服務(wù)的代理孽水,其中就包括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()最終可以簡(jiǎn)化為new WindowManagerImpl(ctx.getDisplay())票腰,下面看下WindowManagerImpl的構(gòu)造方法,它有兩個(gè)實(shí)現(xiàn)方法女气,對(duì)于Activity跟Application其實(shí)是有區(qū)別的杏慰,這點(diǎn)后面分析:

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

private WindowManagerImpl(Display display, Window parentWindow) {
    mDisplay = display;
    mParentWindow = parentWindow;
}

對(duì)于Application采用的是一參構(gòu)造方法,所以其mParentWindow=null炼鞠,這點(diǎn)后面會(huì)有用缘滥,到這里,通過(guò)getService獲取WMS代理的封裝類(lèi)谒主,接著看第二點(diǎn)朝扼,WindowManager.LayoutParams,主要看一個(gè)type參數(shù)霎肯,這個(gè)參數(shù)決定了窗口的類(lèi)型擎颖,這里我們定義成一個(gè)Toast窗口,屬于系統(tǒng)窗口观游,不需要處理父窗口搂捧、子窗口之類(lèi)的事,更容易分析懂缕,最后看關(guān)鍵點(diǎn)3允跑,利用WindowManagerImpl的addView方法添加View到WMS,

 @Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

不過(guò)很明顯WindowManagerImpl最后是委托mGlobal來(lái)進(jìn)行這項(xiàng)操作,WindowManagerGlobal是一個(gè)單利聋丝,一個(gè)進(jìn)程只有一個(gè):

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

接著看WindowManagerGlobal的addView荤崇,對(duì)于添加系統(tǒng)窗口,這里將將代碼精簡(jiǎn)一下潮针,不關(guān)系子窗口等之類(lèi)的邏輯

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        <!--關(guān)鍵點(diǎn)1-->
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }
   <!--關(guān)鍵點(diǎn)2-->
    try {
        root.setView(view, wparams, panelParentView);
    }           
     ...  }

先看關(guān)鍵點(diǎn)1,在向WMS添加View的時(shí)候倚喂,WindowManagerGlobal首先為View新建了一個(gè)ViewRootImpl每篷,ViewRootImpl可以看做也是Window和View之間的通信的紐帶,比如將View添加到WMS端圈、處理WMS傳入的觸摸事件焦读、通知WMS更新窗口大小等、同時(shí)ViewRootImpl也封裝了View的繪制與更新方法等舱权〈;危看一下ViewRootImpl如何通過(guò)setView將視圖添加到WMS的:

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                  ...
                    <!--關(guān)鍵點(diǎn)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 {
                    <!--關(guān)鍵點(diǎn)2 -->
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
               ...

先看關(guān)鍵點(diǎn)1,這里是先為relayout占一個(gè)位置宴倍,其實(shí)是依靠Handler先發(fā)送一個(gè)Message张症,排在所有WMS發(fā)送過(guò)來(lái)的消息之前,先布局繪制一次鸵贬,之后才會(huì)處理WMS傳來(lái)的各種事件俗他,比如觸摸事件等,畢竟要首先將各個(gè)View的布局阔逼、位置處理好兆衅,才能準(zhǔn)確的處理WMS傳來(lái)的事件。接著看做關(guān)鍵點(diǎn)2嗜浮,這里才是真正添加窗口的地方羡亩,雖然關(guān)鍵點(diǎn)1執(zhí)行在前,但是用的是Handler發(fā)消息的方式來(lái)處理危融,其Runable一定是在關(guān)鍵點(diǎn)2之后執(zhí)行畏铆,接著看關(guān)鍵點(diǎn)2,這里有個(gè)比較重要的對(duì)象mWindowSession與mWindow专挪,兩者都是在ViewRootImpl在實(shí)例化的時(shí)候創(chuàng)建的:

public ViewRootImpl(Context context, Display display) {
    mContext = context;
    mWindowSession = WindowManagerGlobal.getWindowSession();
    mWindow = new W(this);

mWindowSession它是通過(guò)WindowManagerGlobal.getWindowSession獲得的一個(gè)Binder服務(wù)代理及志,是App端向WMS發(fā)送消息的通道。相對(duì)的寨腔,mWindow是一個(gè)W extends IWindow.Stub Binder服務(wù)對(duì)象速侈,其實(shí)可以看做是App端的窗口對(duì)象,主要作用是傳遞給WMS迫卢,并作為WMS向APP端發(fā)送消息的通道倚搬,在Android系統(tǒng)中存在大量的這種互為C\S的場(chǎng)景。接著看mWindowSession獲取的具體操作是:首先通過(guò)getWindowManagerService 獲取WMS的代理乾蛤,之后通過(guò)WMS的代理在服務(wù)端open一個(gè)Session每界,并在APP端獲取該Session的代理:

 public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager imm = InputMethodManager.getInstance();
                <!--關(guān)鍵點(diǎn)1-->
                IWindowManager windowManager = getWindowManagerService();
                <!--關(guān)鍵點(diǎn)2-->
                sWindowSession = windowManager.openSession(***)
                ...
        return sWindowSession;
    }
}

看關(guān)鍵點(diǎn)1 :首先要記住sWindowSession是一個(gè)單例的對(duì)象捅僵,之后就可以將getWindowManagerService函數(shù)其實(shí)可以簡(jiǎn)化成下面一句代碼,其實(shí)就是獲得WindowManagerService的代理眨层,之前的WindowManagerImpl都是一個(gè)殼子庙楚,或者說(shuō)接口封裝,并未真正的獲得WMS的代理:

    IWindowManager.Stub.asInterface(ServiceManager.getService("window"))

再看關(guān)鍵點(diǎn)2:sWindowSession = windowManager.openSession趴樱,它通過(guò)binder驅(qū)動(dòng)后馒闷,會(huì)通知WMS回調(diào)openSession,打開(kāi)一個(gè)Session返回給APP端叁征,而Session extends IWindowSession.Stub 纳账,很明顯也是一個(gè)Binder通信的Stub端,封裝了每一個(gè)Session會(huì)話的操作捺疼。

@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來(lái)add一個(gè)窗口:其實(shí)是調(diào)用Session.java的addToDisplayWithoutInputChannel函數(shù)

@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);
}

不過(guò)它又反過(guò)來(lái)去調(diào)用WMS的addWindow,繞這么大一圈啤呼,并且APP端IWindowSession還是單例的卧秘,為什么不直接用WMS來(lái)處理呢?疑惑官扣,在WMS中addWindow又做了什么呢斯议,就像名字寫(xiě)的,負(fù)責(zé)添加一個(gè)窗口醇锚,代碼精簡(jiǎn)后如下:

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) {
        ...
        <!--關(guān)鍵點(diǎn)1 不能重復(fù)添加-->
            if (mWindowMap.containsKey(client.asBinder())) {
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
        <!--關(guān)鍵點(diǎn)2 對(duì)于子窗口類(lèi)型的處理 1哼御、必須有父窗口 2,父窗口不能是子窗口類(lèi)型-->
            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;
            <!--關(guān)鍵點(diǎn)3 根據(jù)IWindow 獲取WindowToken WindowToken是窗口分組的基礎(chǔ)焊唬,每個(gè)窗口必定有一個(gè)分組-->
            WindowToken token = mTokenMap.get(attrs.token);
          <!--關(guān)鍵點(diǎn)4對(duì)于Toast類(lèi)系統(tǒng)窗口恋昼,其attrs.token可以看做是null, 如果目前沒(méi)有其他的類(lèi)似系統(tǒng)窗口展示赶促,token仍然獲取不到液肌,仍然要走新建流程-->
            if (token == null) {
            ...    
                token = new WindowToken(this, attrs.token, -1, false);
                addToken = true;
            } 
            ...
             <!--關(guān)鍵點(diǎn)5 新建WindowState,WindowState與窗口是一對(duì)一的關(guān)系鸥滨,可以看做是WMS中與窗口的抽象實(shí)體-->
            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);
            ... 
           <!--關(guān)鍵點(diǎn)6-->
          addWindowToListInOrderLocked(win, true);
        return res;
    }

這里有幾個(gè)概念需要先了解下:

  • IWindow:APP端窗口暴露給WMS的抽象實(shí)例嗦哆,在ViewRootImpl中實(shí)例化,與ViewRootImpl一一對(duì)應(yīng)婿滓,同時(shí)也是WMS向APP端發(fā)送消息的Binder通道老速。
  • WindowState:WMS端窗口的令牌,與IWindow凸主,或者說(shuō)與窗口一一對(duì)應(yīng)橘券,是WMS管理窗口的重要依據(jù)。
  • WindowToken:窗口的令牌,其實(shí)也可以看做窗口分組的依據(jù)旁舰,在WMS端锋华,與分組對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)是WindowToken(窗口令牌),而與組內(nèi)每個(gè)窗口對(duì)應(yīng)的是WindowState對(duì)象箭窜,每塊令牌(AppWindowToken毯焕、WindowToken)都對(duì)應(yīng)一組窗口(WindowState),Activity與Dialog對(duì)應(yīng)的是AppWindowToken磺樱,PopupWindow對(duì)應(yīng)的是普通的WindowToken芥丧。
  • AppToken:其實(shí)是ActivityRecord里面的IApplicationToken.Stub appToken 代理,也是ActivityClientRecord里面的token坊罢,可以看做Activity在其他服務(wù)(非AMS)的抽象
WindowToken與WindowState關(guān)系.jpg

那么接著關(guān)鍵點(diǎn)1:一個(gè)窗口不能被添加兩次,IWindow是一個(gè)Binder代理擅耽,在WMS端活孩,一個(gè)窗口只會(huì)有一個(gè)IWindow代理,這是由Binder通信機(jī)制保證的乖仇,這個(gè)對(duì)象不能被添加兩次憾儒,否則會(huì)報(bào)錯(cuò)。關(guān)鍵點(diǎn)2乃沙,如果是子窗口的話起趾,父窗口必須已被添加,由于我們分析的是系統(tǒng)Toast窗口警儒,可以先不用關(guān)心训裆;關(guān)鍵點(diǎn)3,WindowManager.LayoutParams中有一個(gè)token字段蜀铲,該字段標(biāo)志著窗口的分組屬性边琉,比如Activity及其中的Dialog是復(fù)用用一個(gè)AppToken,Activity里的PopupWindow復(fù)用一個(gè)IWindow類(lèi)型Token记劝,其實(shí)就是Activity的ViewRootImpl里面創(chuàng)建的IWindow变姨,而對(duì)于我們現(xiàn)在添加的Toast類(lèi)系統(tǒng)窗口,并未設(shè)置其attrs.token厌丑,那即是null定欧,其實(shí)所有的Toast類(lèi)系統(tǒng)窗口的attrs.token都可以看做null,就算不是null怒竿,也會(huì)在WMS被強(qiáng)制設(shè)置為null砍鸠。所以Toast類(lèi)系統(tǒng)窗口必定復(fù)用一個(gè)WindowToken,也可以說(shuō)所有的Toast類(lèi)系統(tǒng)窗口都是位于同一分組耕驰,這也是因?yàn)樵擃?lèi)型系統(tǒng)窗口太常用睦番,而且為所有進(jìn)程服務(wù),直接用一個(gè)WindowToken管理更加快捷,畢竟快速新建與釋放WindowToken也算是一種開(kāi)銷(xiāo)托嚣。假設(shè)到我們添加系統(tǒng)窗口的時(shí)候巩检,沒(méi)有任何系統(tǒng)窗口展示,是獲取不到key=null的WindowToken的示启,要新建WindowToken兢哭,并且添加到全局的TokenMap中,而關(guān)鍵點(diǎn)5夫嗓,其實(shí)就是新建窗口在WMS端的抽象實(shí)例:WindowState迟螺,它同窗口一一對(duì)應(yīng),詳細(xì)記錄了窗口的參數(shù)舍咖、Z順序矩父、狀態(tài)等各種信息,新建只有會(huì)被放入全局的Map中排霉,同時(shí)也會(huì)被附加到相應(yīng)的WindowToken分組中去窍株,到這里APP端向WMS注冊(cè)窗口的流程就算走完了,不過(guò)只算完成了前半部分攻柠,WMS還需要向SurfaceFlinger申請(qǐng)Surface球订,才算完成真正的分配了窗口。在向SurfaceFlinger申請(qǐng)Surface之前瑰钮,WMS端需要獲得SF的代理冒滩,在WindowState對(duì)象創(chuàng)建后會(huì)利用 win.attach()函數(shù)為當(dāng)前APP申請(qǐng)建立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對(duì)于Session來(lái)說(shuō)是單利的,也就是與APP的Seesion一一對(duì)應(yīng)浪谴,SurfaceSession所握著的SurfaceFlinger的代理其實(shí)就是SurfaceComposerClient开睡,其實(shí)現(xiàn)如下:

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

    static jlong nativeCreate(JNIEnv* env, jclass clazz) {
        SurfaceComposerClient* client = new SurfaceComposerClient();
        client->incStrong((void*)nativeCreate);
        return reinterpret_cast<jlong>(client);
    }

Session與APP進(jìn)程是一一對(duì)應(yīng)的,它會(huì)進(jìn)一步為當(dāng)前進(jìn)程建立SurfaceSession會(huì)話苟耻,可以這么理解:Session是APP同WMS通信的通道士八,SurfaceSession是WMS為APP向SurfaceFlinger申請(qǐng)的通信通道,同樣 SurfaceSession與APP也是一一對(duì)應(yīng)的梁呈,既然是同SurfaceFlinger通信的信使婚度,那么SurfaceSession就應(yīng)該握著SurfaceFlinger的代理,其實(shí)就是SurfaceComposerClient里的ISurfaceComposerClient mClient對(duì)象官卡,它是SurfaceFlinger為每個(gè)APP封裝一個(gè)代理蝗茁,也就是 **進(jìn)程 <-> Session <-> SurfaceSession <-> SurfaceComposerClient <-> ISurfaceComposerClient(BpSurfaceComposerClient) **五者是一條線, 為什么不直接與SurfaceFlinger通信呢?大概為了SurfaceFlinger管理每個(gè)APP的Surface比較方便吧寻咒,這四個(gè)類(lèi)的模型如下圖:

Session及SurfaceComposerClient類(lèi)圖

至于ISurfaceComposerClient(BpSurfaceComposerClient) 究竟是怎么樣一步步創(chuàng)建的哮翘,其實(shí)它是利用ComposerService這樣一個(gè)單利對(duì)象為為每個(gè)APP在WMS端申請(qǐng)一個(gè)ISurfaceComposerClient對(duì)象,在WMS端表現(xiàn)為BpSurfaceComposerClient毛秘,在SurfaceFlinger端表現(xiàn)為BnSurfaceComposerClient饭寺,具體代碼如下:

SurfaceComposerClient::SurfaceComposerClient()
    : mStatus(NO_INIT), mComposer(Composer::getInstance())
{
}
// 單利的阻课,所以只有第一次的時(shí)候采用
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;
}
SurfaceComposer類(lèi)圖

剛才說(shuō)完成了前半部分,主要針對(duì)WMS的窗口管理艰匙,后半部分則是圍繞Surface的分配來(lái)進(jìn)行的限煞,還記得之前ViewRootImpl在setView時(shí)候分了兩步嗎?雖然先調(diào)用requestLayout先執(zhí)行员凝,但是由于其內(nèi)部利用Handler發(fā)送消息延遲執(zhí)行的署驻,所以可以看做requestLayout是在addWindow之后執(zhí)行的,那么這里就看添加窗口之后健霹,如何分配Surface的旺上,requestLayout函數(shù)調(diào)用里面使用了Hanlder的一個(gè)小手段,那就是利用postSyncBarrier添加了一個(gè)Barrier(擋板)糖埋,這個(gè)擋板的作用是阻塞普通的同步消息的執(zhí)行宣吱,在擋板被撤銷(xiāo)之前,只會(huì)執(zhí)行異步消息瞳别,而requestLayout先添加了一個(gè)擋板Barrier征候,之后自己插入了一個(gè)異步任務(wù)mTraversalRunnable,其主要作用就是保證mTraversalRunnable在所有同步Message之前被執(zhí)行洒试,保證View繪制的最高優(yōu)先級(jí)。具體實(shí)現(xiàn)如下:

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

            <!--關(guān)鍵點(diǎn)1 添加塞子-->
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        <!--關(guān)鍵點(diǎn)2 添加異步消息任務(wù)-->
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...

mTraversalRunnable任務(wù)的主要作用是:如果Surface未分配朴上,則請(qǐng)求分配Surface垒棋,并測(cè)量、布局痪宰、繪圖叼架,其執(zhí)行主體其實(shí)是performTraversals()函數(shù),該函數(shù)包含了APP端View繪制大部分的邏輯, performTraversals函數(shù)很長(zhǎng)衣撬,這里只簡(jiǎn)要看幾個(gè)點(diǎn),其實(shí)主要是關(guān)鍵點(diǎn)1:relayoutWindow:

private void performTraversals() {
            final View host = mView;
             ...
    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null) {
            <!--關(guān)鍵點(diǎn)1 申請(qǐng)Surface或者重新設(shè)置參數(shù)-->
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
          <!--關(guān)鍵點(diǎn)2 測(cè)量-->
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            }        
          <!--關(guān)鍵點(diǎn)3 布局-->
                performLayout(lp, desiredWindowWidth, desiredWindowHeight);
           <!--關(guān)鍵點(diǎn)4 更新window-->
              try {
                mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
                        contentInsets, visibleInsets, touchableRegion);
            ...
          <!--關(guān)鍵點(diǎn)5 繪制-->
           performDraw();
           ...  
       }

relayoutWindow主要是通過(guò)mWindowSession.relayout向WMS申請(qǐng)或者更新Surface如下乖订,這里只關(guān)心一個(gè)重要的參數(shù)mSurface,在Binder通信中mSurface是一個(gè)out類(lèi)型的參數(shù)具练,也就是Surface內(nèi)部的內(nèi)容需要WMS端負(fù)責(zé)填充乍构,并回傳給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申請(qǐng)Surface的。我們知道每個(gè)窗口都有一個(gè)WindowState與其對(duì)應(yīng)扛点,另外每個(gè)窗口也有自己的動(dòng)畫(huà)哥遮,比如入場(chǎng)/出廠動(dòng)畫(huà),而WindowStateAnimator就是與WindowState的動(dòng)畫(huà)陵究,為什么要提WindowStateAnimator眠饮,因?yàn)閃indowStateAnimator是

 public int relayoutWindow(Session session, IWindow client, int seq,... Surface outSurface) {
         WindowState win = windowForClientLocked(session, client, false);
        WindowStateAnimator winAnimator = win.mWinAnimator;
         <!--關(guān)鍵點(diǎn)1 -->
           SurfaceControl surfaceControl = winAnimator.createSurfaceLocked();
           if (surfaceControl != null) {
         <!--關(guān)鍵點(diǎn)2 -->
             outSurface.copyFrom(surfaceControl);
                } else {
                    outSurface.release();
                }

這里只看Surface創(chuàng)建代碼,首先通過(guò)windowForClientLocked找到WindowState铜邮,利用WindowState的WindowStateAnimator成員創(chuàng)建一個(gè)SurfaceControl仪召,SurfaceControl會(huì)調(diào)用native函數(shù)nativeCreate(session, name, w, h, format, flags)創(chuàng)建Surface寨蹋,

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

關(guān)鍵點(diǎn)1是取到SurfaceSession對(duì)象中SurfaceComposerClient對(duì)象,之后調(diào)用SurfaceComposerClient的createSurface方法進(jìn)一步創(chuàng)建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;
        <!--關(guān)鍵點(diǎn)1 獲取圖層的關(guān)鍵信息handle, gbp-->
        status_t err = mClient->createSurface(name, w, h, format, flags,
                &handle, &gbp);
         <!--關(guān)鍵點(diǎn)2 根據(jù)返回的圖層關(guān)鍵信息 創(chuàng)建SurfaceControl對(duì)象-->
        if (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);
        }
    }
    return sur;
}

這里先看mClient->createSurface已旧,SurfaceComposerClient的mClient其實(shí)是一個(gè)BpSurfaceComposerClient對(duì)象,它SurfaceFlinger端Client在WMS端的代理咖摹,因此創(chuàng)建Surface的代碼還是在SurfaceFlinger服務(wù)端的Client對(duì)象中评姨,這里有兩個(gè)關(guān)鍵的變量sp<IBinder> handle與 sp<IGraphicBufferProducer> gbp,前者標(biāo)志在SurfaceFlinger端的圖層萤晴,后者用來(lái)創(chuàng)建GraphicBuffer吐句,兩者類(lèi)型都是IBinder類(lèi)型,同時(shí)也是需要SurfaceFlinger填充的對(duì)象店读,這兩者是一個(gè)圖層對(duì)應(yīng)的最關(guān)鍵的信息:

status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp){
    ...
    <!--關(guān)鍵點(diǎn)2 這里并未直接創(chuàng)建 嗦枢,而是通過(guò)發(fā)送了一個(gè)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 并不會(huì)直接新建圖層,而是向SurfaceFlinger發(fā)送一個(gè)MessageCreateLayer消息屯断,通知SurfaceFlinger服務(wù)去執(zhí)行文虏,其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;
        }
    };

其實(shí)就是調(diào)用SurfaceFlinger的createLayer,創(chuàng)建一個(gè)圖層殖演,到這里才是真正的創(chuàng)建圖層:

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;
  <!--關(guān)鍵點(diǎn)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會(huì)根據(jù)不同的窗口參數(shù)氧秘,創(chuàng)建不同類(lèi)型的圖層,這里只看一下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;
    }
    <!--關(guān)鍵點(diǎn) 1 -->
    *outLayer = new Layer(this, client, name, w, h, flags);
    status_t err = (*outLayer)->setBuffers(w, h, format, flags);
    <!--關(guān)鍵點(diǎn) 2-->
    if (err == NO_ERROR) {
        *handle = (*outLayer)->getHandle();
        *gbp = (*outLayer)->getProducer();
    }
  return err;
}

可以看到 圖層最終對(duì)應(yīng)的是Layer丸相,這里會(huì)新建一個(gè)Layer對(duì)象,Layer中包含著與這個(gè)圖層對(duì)應(yīng)的Handle及Producer對(duì)象彼棍,Handle可以看做是Surface的唯一性標(biāo)識(shí)灭忠,不過(guò)好像沒(méi)太大的作用,最多是一個(gè)標(biāo)識(shí)座硕,將來(lái)清理的時(shí)候有用弛作。相比之下gbp = (*outLayer)->getProducer()比較重要,它實(shí)際是一個(gè)BufferQueueProducer對(duì)象华匾,關(guān)系到共享內(nèi)存的分配問(wèn)題映琳,后面會(huì)專(zhuān)門(mén)分析,這里到此打住蜘拉,我們終于得到了一個(gè)圖層對(duì)象刊头,到這里之后,我們梳理一下诸尽,圖層如何建立的:

  • 首先APP端新建一個(gè)Surface圖層的容器殼子原杂,
  • APP通過(guò)Binder通信將這個(gè)Surface的殼子傳遞給WMS,
  • WMS為了填充Surface去向SurfaceFlinger申請(qǐng)真正的圖層您机,
  • SurfaceFlinger收到WMS請(qǐng)求為APP端的Surface分配真正圖層
  • 將圖層相關(guān)的關(guān)鍵信息Handle及Producer傳遞給WMS

Layer建立之后穿肄,SurfaceFlinger會(huì)將圖層標(biāo)識(shí)信息Handle及Producer傳遞給WMS年局,WMS利用這兩者創(chuàng)建一個(gè)SurfaceControl對(duì)象,之后再利用該對(duì)象創(chuàng)建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的拷貝函數(shù)其實(shí)就是直接修改Surface native對(duì)象指針值矢否,native的Surface對(duì)象中包含mGraphicBufferProducer對(duì)象,很重要脑溢,會(huì)被傳遞給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創(chuàng)建及填充完畢,并且Surface其實(shí)與WMS的SurfaceControl一一對(duì)應(yīng)屑彻,當(dāng)APP端需要在圖層級(jí)別進(jìn)行操控的時(shí)候验庙,其實(shí)還是要依靠SurfaceControl的,WMS的Surface創(chuàng)建完畢后社牲,需要傳遞給APP端粪薛,之后APP端就獲得直接同SurfaceFlinger通信的能力,比如繪圖與UI更新搏恤,怎傳遞的呢违寿?我們知道Surface實(shí)現(xiàn)了Parcel接口,因此可以傳遞序列化的數(shù)據(jù)熟空,其實(shí)看一下Surface nativeReadFromParcel就知道到底是怎么傳遞的了藤巢,利用readStrongBinder獲取IGraphicBufferProducer對(duì)象的句柄,之后轉(zhuǎn)化為IGraphicBufferProducer代理其實(shí)就是BpGraphicBufferProducer息罗,之后利用BpGraphicBufferProducer構(gòu)建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 通信申請(qǐng)Surface的流程算走完了

Surface對(duì)應(yīng)關(guān)系.jpg

總結(jié)

窗口的添加流程簡(jiǎn)化如下俏扩,這里暫且忽略窗口的分組管理糜工。

  • APP首先去WMS登記窗口
  • WMS端登記窗口
  • APP新建Surface殼子弊添,請(qǐng)求WMS填充Surface
  • WMS請(qǐng)求SurfaceFlinger分配窗口圖層
  • SurfaceFlinger分配Layer,將結(jié)果回傳給WMS
  • WMS將窗口信息填充到Surface傳輸?shù)紸PP
  • APP端獲得填充信息捌木,獲取與SurfaceFlinger通信的能力

作者:看書(shū)的小蝸牛
原文鏈接: WindowManagerService窗口管理之Window添加流程

僅供參考油坝,歡迎指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市刨裆,隨后出現(xiàn)的幾起案子澈圈,更是在濱河造成了極大的恐慌,老刑警劉巖帆啃,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬女,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡努潘,警方通過(guò)查閱死者的電腦和手機(jī)诽偷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)坤学,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人报慕,你說(shuō)我怎么就攤上這事深浮。” “怎么了眠冈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵飞苇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蜗顽,道長(zhǎng)布卡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任诫舅,我火速辦了婚禮羽利,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刊懈。我一直安慰自己这弧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布虚汛。 她就那樣靜靜地躺著匾浪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卷哩。 梳的紋絲不亂的頭發(fā)上蛋辈,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音将谊,去河邊找鬼冷溶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尊浓,可吹牛的內(nèi)容都是我干的逞频。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼栋齿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼苗胀!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瓦堵,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤基协,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后菇用,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體澜驮,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年惋鸥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杂穷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹅龄。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亭畜,靈堂內(nèi)的尸體忽然破棺而出扮休,到底是詐尸還是另有隱情,我是刑警寧澤拴鸵,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布玷坠,位于F島的核電站,受9級(jí)特大地震影響劲藐,放射性物質(zhì)發(fā)生泄漏八堡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一聘芜、第九天 我趴在偏房一處隱蔽的房頂上張望兄渺。 院中可真熱鬧,春花似錦汰现、人聲如沸挂谍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)口叙。三九已至,卻和暖如春嗅战,著一層夾襖步出監(jiān)牢的瞬間妄田,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工驮捍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疟呐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓东且,卻偏偏與公主長(zhǎng)得像启具,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苇倡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容