Window和WindowManager淺析

介紹

Window表示一個窗口的概念,它是一個抽象類,其唯一的實(shí)現(xiàn)類是PhoneWindow。Android中所有視圖的顯示都需要通過Window來呈現(xiàn),可以說只要有視圖的地方就有Window,比如Activity,Dialog它們的視圖也是需要附加在Window上顯示的,所以Window可以說是View的直接管理者。

WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Button button = new Button(this);
button.setText("我是Button");
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.FIRST_SUB_WINDOW, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT
);
windowManager.addView(button, layoutParams);

以上就是用WindowManager添加一個Window的過程,addView方法有兩個參數(shù),第一個是要添加的View,第二個是WindowManager.LayoutParams對象,LayoutParams有一個type屬性,它代表要添加的Window的類型,分為三類。

Window的分類

  • 應(yīng)用Window

    Activity的窗口類型就是應(yīng)用Window

  • 子Window

    子Window不能單獨(dú)存在,需要依附于父Window上,如Dialog、PopWindow等

  • 系統(tǒng)Window

    系統(tǒng)Window是系統(tǒng)層級的Window,如狀態(tài)欄、Toast等

Window是分層顯示的,每個Window都有其對應(yīng)的z-ordered,層級大的會覆蓋在層級小的上面。其中應(yīng)用Window的層級范圍為1-99,子Window層級范圍為1000-1999,系統(tǒng)Window層級范圍為2000-2999,該層級范圍對應(yīng)著WindowManager.LayoutParams的type屬性。上面添加Window的type為FIRST_SUB_WINDOW,它是子Window的初始取值,所以我們添加的是一個子Window。

Window的內(nèi)部機(jī)制

我們在使用WindowManager添加Window時,其實(shí)是添加了一個View。Window作為一個抽象概念,它并不是實(shí)際存在的,而是以View的形式存在的,每一個Window都對應(yīng)著一個View和一個ViewRootImpl,Window和View通過ViewRootImpl來建立聯(lián)系。

WindowManager接口繼承了ViewManager接口,在ViewManager中分別定義了添加,更新和刪除Window的方法

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

分別來看Window的添加,更新和刪除過程

Window的添加

WindowManager的實(shí)現(xiàn)類是WindowManagerImpl,addView方法

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

這里沒有具體實(shí)現(xiàn),轉(zhuǎn)而調(diào)用mGlobal的方法,mGlobal為WindowManagerGlobal對象

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    //...

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
                //...
        int index = findViewLocked(view, false);
        if (index >= 0) {
            if (mDyingViews.contains(view)) {
                // Don't wait for MSG_DIE to make it's way through root's queue.
                mRoots.get(index).doDie();
            } else {
                throw new IllegalStateException("View " + view
                        + " has already been added to the window manager.");
            }
            // The previous removeView() had not completed executing. Now it has.
        }

        // If this is a panel window, then find the window it is being
        // attached to for future reference.
        if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            final int count = mViews.size();
            for (int i = 0; i < count; i++) {
                if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                    panelParentView = mViews.get(i);
                }
            }
        }

        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) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}
  • 首先做View等的非空判斷以及LayoutParams的類型判斷,然后會查找當(dāng)前添加的View是否已經(jīng)存在,如果已經(jīng)存在并且它在等待移除隊(duì)列中的話,直接將其移除,否則拋出View已經(jīng)添加過的異常。判斷添加的Window類型為子Window的話,還要找到它所附屬的父Window。
  • 初始化ViewRootImpl對象,并將LayoutParams設(shè)給View。
  • 將View,ViewRootImpl以及LayoutParams對象添加到對應(yīng)的集合中。
  • 調(diào)用ViewRootImpl的setView方法完成布局的刷新以及Window的添加。

ViewRootImpl的setView方法

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            //...
            requestLayout();
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
            //...
        }    
    }
}
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

在requestLayout方法中調(diào)用了scheduleTraversals,而這個scheduleTraversals就是View繪制流程的起點(diǎn),在這里完成了View樹的繪制。

mWindowSession是一個Binder對象,它的實(shí)現(xiàn)是Session

@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

最后調(diào)用WindowManagerService的addWindow方法完成了Window的添加。

Window的更新

Window的更新過程,直接來看WindowManagerGlobal的updateViewLayout方法

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    //...
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

    view.setLayoutParams(wparams);

    synchronized (mLock) {
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots.get(index);
        mParams.remove(index);
        mParams.add(index, wparams);
        root.setLayoutParams(wparams, false);
    }
}

將新的LayoutParams設(shè)置給View,找到當(dāng)前View的index索引,根據(jù)index移除舊的LayoutParams并將新的LayoutParams添加到集合中。調(diào)用ViewRootImpl的setLayoutParams方法

void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
  //...
  scheduleTraversals();
  //...
}

這里調(diào)用scheduleTraversals方法對布局進(jìn)行重繪,完成了Window的更新。

Window的刪除

WindowManagerGlobal的removeView方法

public void removeView(View view, boolean immediate) {
    //...
    synchronized (mLock) {
        int index = findViewLocked(view, true);
        View curView = mRoots.get(index).getView();
        removeViewLocked(index, immediate);
        //...
    }
}

獲取要移除View的索引,然后調(diào)用removeViewLocked方法

private void removeViewLocked(int index, boolean immediate) {
    ViewRootImpl root = mRoots.get(index);
    View view = root.getView();
        //...
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
            mDyingViews.add(view);
        }
    }
}

調(diào)用ViewRootImpl的die方法

boolean die(boolean immediate) {
    //...
    if (immediate && !mIsInTraversal) {
        doDie();
        return false;
    }
        //...
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}

immediate參數(shù)表示是否要立即移除,即同步還是異步。我們一般采用異步移除的方式,異步移除使用mHandler發(fā)送MSG_DIE消息,然后返回true,在removeViewLocked方法中將該View添加到等待移除的列表mDyingViews中。mHandler中處理MSG_DIE消息的方法為doDie

void doDie() {
  //...
  if (mAdded) {
      dispatchDetachedFromWindow();
     }
    //...
  WindowManagerGlobal.getInstance().doRemoveView(this);
  //...
}
void dispatchDetachedFromWindow() {
    if (mView != null && mView.mAttachInfo != null) {
        mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
        mView.dispatchDetachedFromWindow();
    }
  //...
    try {
          mWindowSession.remove(mWindow);
        } catch (RemoteException e) {
        }
    //...
}

在dispatchDetachedFromWindow方法中會回調(diào)View的onDetachedFromWindow方法,以及調(diào)用Session的remove方法,該方法最終還是調(diào)用WMS的removeWindow方法完成Window的移除。

void doRemoveView(ViewRootImpl root) {
    synchronized (mLock) {
        final int index = mRoots.indexOf(root);
        if (index >= 0) {
            mRoots.remove(index);
            mParams.remove(index);
            final View view = mViews.remove(index);
            mDyingViews.remove(view);
        }
    }
    //...
}

最后在doRemoveView方法中刷新數(shù)據(jù),移除對應(yīng)的ViewRootImpl,LayoutParams以及將View從mDyingViews中移除,這就完成了Window的刪除。

可以發(fā)現(xiàn)不管是添加,更新還是刪除Window,最后都是在操作View,所以這就能證明Window并不是實(shí)際存在的,它是以View的形式存在,而Window是View的直接管理者。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容