美文网首页
Window和WindowManager浅析

Window和WindowManager浅析

作者: m1Ku | 来源:发表于2019-08-05 15:52 被阅读0次

    介绍

    Window表示一个窗口的概念,它是一个抽象类,其唯一的实现类是PhoneWindow。Android中所有视图的显示都需要通过Window来呈现,可以说只要有视图的地方就有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方法有两个参数,第一个是要添加的View,第二个是WindowManager.LayoutParams对象,LayoutParams有一个type属性,它代表要添加的Window的类型,分为三类。

    Window的分类

    • 应用Window

      Activity的窗口类型就是应用Window

    • 子Window

      子Window不能单独存在,需要依附于父Window上,如Dialog、PopWindow等

    • 系统Window

      系统Window是系统层级的Window,如状态栏、Toast等

    Window是分层显示的,每个Window都有其对应的z-ordered,层级大的会覆盖在层级小的上面。其中应用Window的层级范围为1-99,子Window层级范围为1000-1999,系统Window层级范围为2000-2999,该层级范围对应着WindowManager.LayoutParams的type属性。上面添加Window的type为FIRST_SUB_WINDOW,它是子Window的初始取值,所以我们添加的是一个子Window。

    Window的内部机制

    我们在使用WindowManager添加Window时,其实是添加了一个View。Window作为一个抽象概念,它并不是实际存在的,而是以View的形式存在的,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。

    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的实现类是WindowManagerImpl,addView方法

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

    这里没有具体实现,转而调用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的类型判断,然后会查找当前添加的View是否已经存在,如果已经存在并且它在等待移除队列中的话,直接将其移除,否则抛出View已经添加过的异常。判断添加的Window类型为子Window的话,还要找到它所附属的父Window。
    • 初始化ViewRootImpl对象,并将LayoutParams设给View。
    • 将View,ViewRootImpl以及LayoutParams对象添加到对应的集合中。
    • 调用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方法中调用了scheduleTraversals,而这个scheduleTraversals就是View绘制流程的起点,在这里完成了View树的绘制。

    mWindowSession是一个Binder对象,它的实现是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
    

    最后调用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设置给View,找到当前View的index索引,根据index移除旧的LayoutParams并将新的LayoutParams添加到集合中。调用ViewRootImpl的setLayoutParams方法

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

    这里调用scheduleTraversals方法对布局进行重绘,完成了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的索引,然后调用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);
            }
        }
    }
    

    调用ViewRootImpl的die方法

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

    immediate参数表示是否要立即移除,即同步还是异步。我们一般采用异步移除的方式,异步移除使用mHandler发送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方法中会回调View的onDetachedFromWindow方法,以及调用Session的remove方法,该方法最终还是调用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方法中刷新数据,移除对应的ViewRootImpl,LayoutParams以及将View从mDyingViews中移除,这就完成了Window的删除。

    可以发现不管是添加,更新还是删除Window,最后都是在操作View,所以这就能证明Window并不是实际存在的,它是以View的形式存在,而Window是View的直接管理者。

    相关文章

      网友评论

          本文标题:Window和WindowManager浅析

          本文链接:https://www.haomeiwen.com/subject/nbpmdctx.html