美文网首页
Window, WindowManager, WindowMan

Window, WindowManager, WindowMan

作者: 十二书 | 来源:发表于2017-11-08 18:29 被阅读0次

    Window, WindowManager, WindowManagerService 的简单梳理(一)

    Window, WindowManager, WindowManagerService 的简单梳理(三)- Activiy 的 Window 的创建过程

    通过 Window, WindowManager, WindowManagerService 的简单梳理(一) 已经知道,我们能够从 Activity 或者 Context 拿到的,就是 WindowManager 的具体实现 WindowManagerImpl。
    当我们想要添加一个 Window 的时候,就是要通过 WindowManagerImpl 的方法来实现。更准确的说,是 WindowManagerImpl 实现的 ViewManager 接口的方法。

    public interface ViewManager
    {
        /**
         * Assign the passed LayoutParams to the passed View and add the view to the window.
         * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
         * errors, such as adding a second view to a window without removing the first view.
         * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
         * secondary {@link Display} and the specified display can't be found
         * (see {@link android.app.Presentation}).
         * @param view The view to be added to this window.
         * @param params The LayoutParams to assign to view.
         */
        public void addView(View view, ViewGroup.LayoutParams params);
        public void updateViewLayout(View view, ViewGroup.LayoutParams params);
        public void removeView(View view);
    }
    

    下面的例子是参考任玉刚的《Android 开发艺术探索》,我做了一点修改。

            Button btn = new Button(this);
            btn.setText("WMS Demo");
    
            WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT,
                    1, 0, PixelFormat.TRANSPARENT);
            params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
            params.gravity = Gravity.LEFT | Gravity.TOP;
            params.x = 100;
            params.y = 100;
            getWindowManager().addView(btn, params);
    

    其中,WindowManager.LayoutParams 的 type 和 flags 两个参数比较重要,大家可以参考网上的各种说明。当然,源码里面也有详细的说明。
    我这个例子,是可以直接在 activity 里面跑起来的,作用是在屏幕上坐标为 params.x = 100; params.y = 100; 的地方添加一个按钮。

    下面,我们就跟着源码看看,调用 getWindowManager().addView(btn, params) 后到底发生了什么。

    Window 的添加过程

    // WindowManagerImpl 
    public final class WindowManagerImpl implements WindowManager {
        ...
        private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
        ...
        @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
        ...
    }
    

    可见,WindowManagerImpl 把任务转给了 mGlobal 的 addView 方法。
    看下面的代码,再往下走,就进入了root.setView(view, wparams, panelParentView),也就是 ViewRootImpl 的方法。

    // WindowManagerGlobal 
    public final class WindowManagerGlobal {
        ...
        // 存储所有 Window 对应的 View
        private final ArrayList<View> mViews = new ArrayList<View>();
        // 存储所有 Window 对应的 ViewRootImpl
        private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
        // 存储所有 Window 对应的布局参数
        private final ArrayList<WindowManager.LayoutParams> mParams =
                new ArrayList<WindowManager.LayoutParams>();
        // 存储正在被删除的 View 对象,也就是调用了 removeView 方法,但是删除操作还未完成的 View
        private final ArraySet<View> mDyingViews = new ArraySet<View>();
    
        ...
    
        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");
            }
    
            ...
    
            ViewRootImpl root;
            View panelParentView = null;
            ...
                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.
                synchronized (mLock) {
                    final int index = findViewLocked(view, false);
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                }
                throw e;
            }
        }
    }
    

    ViewRootImpl 的 setView 会最终调用到 mWindowSession.addToDisplay()。

    // ViewRootImpl 
    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks {
        ...
        final IWindowSession mWindowSession;
    
        final W mWindow;
        ...
        public ViewRootImpl(Context context, Display display) {
            mContext = context;
            mWindowSession = WindowManagerGlobal.getWindowSession();
    
            mWindow = new W(this);
            ...
        }
        ...
    
        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                ...
                int res; /* = WindowManagerImpl.ADD_OKAY; */
    
                    // 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();
                    ...
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } catch (RemoteException e) {
                        mAdded = false;
                        mView = null;
                        mAttachInfo.mRootView = null;
                        mInputChannel = null;
                        mFallbackEventHandler.setView(null);
                        unscheduleTraversals();
                        setAccessibilityFocus(null, null);
                        throw new RuntimeException("Adding window failed", e);
                    } finally {
                        if (restore) {
                            attrs.restore();
                        }
                    }
            }
        }
        ...
        @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                checkThread();
                mLayoutRequested = true;
                // scheduleTraversals 是 View 绘制的入口
                scheduleTraversals();
            }
        }
    
        ...
    
        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;
            }
            ...
        }
    }
    

    mWindowSession 的类型是 IWindowSession,它是一个 Binder 对象,真正的实现类是 Session,这是一次 IPC 调用。
    我在 Window, WindowManager, WindowManagerService 的简单梳理(一) 中曾经写过,WindowManagerService 是 IWindowManager 接口的实现。所以,这里才会和 WindowManagerService 发生关系。

    // WindowManagerGlobal
        public static IWindowSession getWindowSession() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowSession == null) {
                    try {
                        InputMethodManager imm = InputMethodManager.getInstance();
                        IWindowManager windowManager = getWindowManagerService();
                        sWindowSession = windowManager.openSession(
                                new IWindowSessionCallback.Stub() {
                                    @Override
                                    public void onAnimatorScaleChanged(float scale) {
                                        ValueAnimator.setDurationScale(scale);
                                    }
                                },
                                imm.getClient(), imm.getInputContext());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowSession;
            }
        }
    
    // Session 
    final class Session extends IWindowSession.Stub
                        implements IBinder.DeathRecipient {
        final WindowManagerService mService;
        ...
        public Session(WindowManagerService service, IWindowSessionCallback callback,
                IInputMethodClient client, IInputContext inputContext) {
            mService = service;
            ...
        }
        ...
        @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);
        }
    }
    

    可见,mWindowSession 内部最终是将 Window 提交给了 WindowManagerService 去处理。WindowManagerService 内部会为每一个应用保留一个单独的 Session。

    如果再仔细看,mWindowSession 提交的是一个 IWindow 对象,也就是 ViewRootImpl 内部的 mWindow对象,类型是 ViewRootImpl.W。

    所以说,什么是 Window?如果 Window 是指用来做显示的一个抽象概念。那么,当一个新的 Window 被添加以后,WindowManagerService 中会增加一个 IWindow 对象。

    值得一提的是,Activity 的 Window 的创建,也基本遵循这个过程,只不过这个过程是自动完成的。

    相关文章

      网友评论

          本文标题:Window, WindowManager, WindowMan

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