关于Window和WindowManager的一点愚见(添加,删除,更新)

来源:互联网 发布:可信的网络兼职 编辑:程序博客网 时间:2024/06/05 19:17

正文

先看看自己要编写一个WindowManager该如何实现。
先看看MainActivity.java

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        button = (Button)findViewById(R.id.floatbutton);        windowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);        button.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                // TODO Auto-generated method stub                createWindowManager();            }        });    }    private void createWindowManager(){        //实现浮窗型Button        floatButton = new Button(this);        floatButton.setText("button");        //view的LayoutParams        layoutParams = new WindowManager.LayoutParams(                LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT,0,0,PixelFormat.TRANSPARENT);        //布局参数        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL                | LayoutParams.FLAG_NOT_FOCUSABLE                | LayoutParams.FLAG_SHOW_WHEN_LOCKED;        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;        layoutParams.x = 300;        layoutParams.y = 300;        layoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;        //添加view到windowmanager        windowManager.addView(floatButton, layoutParams);        //随着触碰改变button的位置        floatButton.setOnTouchListener(new OnTouchListener() {            @Override            public boolean onTouch(View av, MotionEvent ev) {                // TODO Auto-generated method stub                int rawX = (int)ev.getRawX();                int rawY = (int)ev.getRawY();                switch (ev.getAction()) {                case MotionEvent.ACTION_MOVE:                    layoutParams.x = rawX;                    layoutParams.y = rawY;                    windowManager.updateViewLayout(floatButton, layoutParams);                    break;                default:                    break;                }                return false;            }        });    }

从上面的简单范例,我们可以知道我们先要通过windowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);来获取WindowManager的实例,看看源码在Activty.java:

@Override    public Object getSystemService(@ServiceName @NonNull String name) {        if (getBaseContext() == null) {            throw new IllegalStateException(                    "System services not available to Activities before onCreate()");        }        if (WINDOW_SERVICE.equals(name)) {            return mWindowManager;        } else if (SEARCH_SERVICE.equals(name)) {            ensureSearchManager();            return mSearchManager;        }        return super.getSystemService(name);    }

我们可以清晰的看到,这里的确返回了一个windowManager实例。
在上面的例子中我们先定义了即将加入到Window中的view的LayoutParams参数,而在这里LayoutParams指的是WindowManager中内部类ManagerWindow.LayoutParams。
在这里我们先说说Window中flag和type参数,这两个参数比较重要:
先说说比较常见的flag:
1.FLAG_NOT_FOCUSABLE:
表示Window不需要获得焦点,也不需要接受接受各种输入时间,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层具有焦点的Window。

2.FLAG_NOT_TOUCH_MODAL:
在这个模式下,系统会将当前window区域之外单击事件传递给底层的Window,当前Window区域以内的2单击事件则自己处理。这个标记十分重要,一般说都会开启这个标记。

3.FLAG_SHOW_WHEN_LOCKED
开启这个模式可以让Window显示在锁屏的界面。

接着说说Type,type参数表示Window的类型:应用Window(ApplicationWndow),子Window(SubWindow),系统Window(SystemWindow)。

应用Window对应Activity。子Window不能单独存在,它需要附属在特定在特定父Window中,比如说常见的一些Dialog就是一个子Window。系统Window是需要声明权限才能够创建Window,比如Toast和系统状态栏都是系统Window。

在Android中Window是分层的,每个Window都对应这z-ordered,层级大的会覆盖层级小的上满,写过HTML的朋友一定能明白,这个和HTML中z-index的概念一致。

在三种Window中,应用Window的层级范围是1~99,子window的层级范围1000~1999,系统Window层级是2000~2999.这些层级范围对应着WindowManager.LayoutParams的type系数。

  public static final int FIRST_APPLICATION_WINDOW = 1;        /**         * Window type: an application window that serves as the "base" window         * of the overall application; all other application windows will         * appear on top of it.         * In multiuser systems shows only on the owning user's window.         */        public static final int TYPE_BASE_APPLICATION   = 1;        /**         * Window type: a normal application window.  The {@link #token} must be         * an Activity token identifying who the window belongs to.         * In multiuser systems shows only on the owning user's window.         */        public static final int TYPE_APPLICATION        = 2;        /**         * Window type: special application window that is displayed while the         * application is starting.  Not for use by applications themselves;         * this is used by the system to display something until the         * application can show its own windows.         * In multiuser systems shows on all users' windows.         */        public static final int TYPE_APPLICATION_STARTING = 3;        /**         * End of types of application windows.         */        public static final int LAST_APPLICATION_WINDOW = 99;        /**         * Start of types of sub-windows.  The {@link #token} of these windows         * must be set to the window they are attached to.  These types of         * windows are kept next to their attached window in Z-order, and their         * coordinate space is relative to their attached window.         */        public static final int FIRST_SUB_WINDOW        = 1000;        /**         * Window type: a panel on top of an application window.  These windows         * appear on top of their attached window.         */        public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;        /**         * Window type: window for showing media (such as video).  These windows         * are displayed behind their attached window.         */        public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;                public static final int LAST_SUB_WINDOW         = 1999;        /**         * Start of system-specific window types.  These are not normally         * created by applications.         */        public static final int FIRST_SYSTEM_WINDOW     = 2000;

源码也是像Android文档中那般定义。

这里要注意的当我们使用系统Window的时候必须声明权限,不然会报如下错误:

android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@41a1be70 -- permission denied for this window type

我们设定为系统Window的时候,需要将type设置为TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR。如果调用TYPE_SYSTEM_ERROR我们需要声明权限:

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

这样就能完成一次系统Window的创建了。

我们来看看WindowManager中在整个Window的添加,移除,更新扮演了一个什么角色。

public interface WindowManager extends ViewManager 

从上面的源码所示,WindowManager是一个继承了ViewManager接口的接口。而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);}

Window的添加,删除,更新三个动作

Window是一个抽象的概念,每一个Window都对应一个View一个ViewRootImpl,Window和View是通过ViewRootImpl联系起来的。会自定义View的朋友应该都知道,ViewRootImpl才是一个View真正实现的动作。同理在WindowManager中也是一个WindowManagerImpl作为实现的类。

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

mGlobal是指WindowManagerGlobal,这里很明显是通过了两个类的组合来控制动作,使用了桥接模式。我们先看看WindowManagerGlobal的addView方法。
首先对输入的参数进行是否合法的检验:

 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;        if (parentWindow != null) {            parentWindow.adjustLayoutParamsForSubWindow(wparams);        } else {            // If there's no parent and we're running on L or above (or in the            // system context), assume we want hardware acceleration.            final Context context = view.getContext();            if (context != null                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;            }        }

接着创建ViewRootImpl并将View添加到列表中,同时初始化重要的了表,将参数输入到列表中:

            root = new ViewRootImpl(view.getContext(), display);            view.setLayoutParams(wparams);            mViews.add(view);            mRoots.add(root);            mParams.add(wparams);

再通过ViewRootImpl的setView来更新界面完成Window的添加过程:

root.setView(view, wparams, panelParentView);

ViewRootImpl.java中setView工作:

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {            if (mView == null) {                mView = view;                mAttachInfo.mDisplayState = mDisplay.getState();                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);                mViewLayoutDirectionInitial = mView.getRawLayoutDirection();                mFallbackEventHandler.setView(view);                mWindowAttributes.copyFrom(attrs);                if (mWindowAttributes.packageName == null) {                    mWindowAttributes.packageName = mBasePackageName;                }                attrs = mWindowAttributes;                // Keep track of the actual window flags supplied by the client.                mClientWindowLayoutFlags = attrs.flags;                setAccessibilityFocus(null, null);                if (view instanceof RootViewSurfaceTaker) {                    mSurfaceHolderCallback =                            ((RootViewSurfaceTaker)view).willYouTakeTheSurface();                    if (mSurfaceHolderCallback != null) {                        mSurfaceHolder = new TakenSurfaceHolder();                        mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);                    }                }                // Compute surface insets required to draw at specified Z value.                // TODO: Use real shadow insets for a constant max Z.                final int surfaceInset = (int) Math.ceil(view.getZ() * 2);                attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);                CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();                mTranslator = compatibilityInfo.getTranslator();                mDisplayAdjustments.setActivityToken(attrs.token);                // If the application owns the surface, don't enable hardware acceleration                if (mSurfaceHolder == null) {                    enableHardwareAcceleration(attrs);                }                boolean restore = false;                if (mTranslator != null) {                    mSurface.setCompatibilityTranslator(mTranslator);                    restore = true;                    attrs.backup();                    mTranslator.translateWindowLayout(attrs);                }                if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);                if (!compatibilityInfo.supportsScreen()) {                    attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;                    mLastInCompatMode = true;                }                mSoftInputMode = attrs.softInputMode;                mWindowAttributesChanged = true;                mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;                mAttachInfo.mRootView = view;                mAttachInfo.mScalingRequired = mTranslator != null;                mAttachInfo.mApplicationScale =                        mTranslator == null ? 1.0f : mTranslator.applicationScale;                if (panelParentView != null) {                    mAttachInfo.mPanelParentWindowToken                            = panelParentView.getApplicationWindowToken();                }                mAdded = true;                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();                if ((mWindowAttributes.inputFeatures                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {                    mInputChannel = new InputChannel();                }                try {                    mOrigWindowType = mWindowAttributes.type;                    mAttachInfo.mRecomputeGlobalAttributes = true;                    collectViewAttributes();                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(),                            mAttachInfo.mContentInsets, 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();                    }                }                if (mTranslator != null) {                    mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);                }                mPendingOverscanInsets.set(0, 0, 0, 0);                mPendingContentInsets.set(mAttachInfo.mContentInsets);                mPendingStableInsets.set(mAttachInfo.mStableInsets);                mPendingVisibleInsets.set(0, 0, 0, 0);                if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);                if (res < WindowManagerGlobal.ADD_OKAY) {                    mAttachInfo.mRootView = null;                    mAdded = false;                    mFallbackEventHandler.setView(null);                    unscheduleTraversals();                    setAccessibilityFocus(null, null);                    switch (res) {                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:                            throw new WindowManager.BadTokenException(                                "Unable to add window -- token " + attrs.token                                + " is not valid; is your activity running?");                        case WindowManagerGlobal.ADD_NOT_APP_TOKEN:                            throw new WindowManager.BadTokenException(                                "Unable to add window -- token " + attrs.token                                + " is not for an application");                        case WindowManagerGlobal.ADD_APP_EXITING:                            throw new WindowManager.BadTokenException(                                "Unable to add window -- app for token " + attrs.token                                + " is exiting");                        case WindowManagerGlobal.ADD_DUPLICATE_ADD:                            throw new WindowManager.BadTokenException(                                "Unable to add window -- window " + mWindow                                + " has already been added");                        case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:                            // Silently ignore -- we would have just removed it                            // right away, anyway.                            return;                        case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:                            throw new WindowManager.BadTokenException(                                "Unable to add window " + mWindow +                                " -- another window of this type already exists");                        case WindowManagerGlobal.ADD_PERMISSION_DENIED:                            throw new WindowManager.BadTokenException(                                "Unable to add window " + mWindow +                                " -- permission denied for this window type");                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:                            throw new WindowManager.InvalidDisplayException(                                "Unable to add window " + mWindow +                                " -- the specified display can not be found");                    }                    throw new RuntimeException(                        "Unable to add window -- unknown error code " + res);                }                if (view instanceof RootViewSurfaceTaker) {                    mInputQueueCallback =                        ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();                }                if (mInputChannel != null) {                    if (mInputQueueCallback != null) {                        mInputQueue = new InputQueue();                        mInputQueueCallback.onInputQueueCreated(mInputQueue);                    }                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,                            Looper.myLooper());                }                view.assignParent(this);                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;                mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;                if (mAccessibilityManager.isEnabled()) {                    mAccessibilityInteractionConnectionManager.ensureConnection();                }                if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {                    view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);                }                // Set up the input pipeline.                CharSequence counterSuffix = attrs.getTitle();                mSyntheticInputStage = new SyntheticInputStage();                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,                        "aq:native-post-ime:" + counterSuffix);                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);                InputStage imeStage = new ImeInputStage(earlyPostImeStage,                        "aq:ime:" + counterSuffix);                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,                        "aq:native-pre-ime:" + counterSuffix);                mFirstInputStage = nativePreImeStage;                mFirstPostImeInputStage = earlyPostImeStage;                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;            }        }    }

setView完成的工作很多,如声明输入事件的管道,DisplayManager的注册,view的绘画,window的添加等等,其实最重要的方法如下两个:
1.

//绘制view的入口requestLayout();

2.通过WindowSession来完成Window的添加过程下面代码中,是一个IPC的过程。

try {                    mOrigWindowType = mWindowAttributes.type;                    mAttachInfo.mRecomputeGlobalAttributes = true;                    collectViewAttributes();                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(),                            mAttachInfo.mContentInsets, 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();                    }                }

mWindowSession是一个IWindowSession,是一个Binder对象,真正实现的地方是Session。

我们看看Session.java:

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

在这里面调用了WindowManagerService中addWindow的方法,这里就将Window的添加请求交给了WindowManagerService了:

public int addWindow(Session session, IWindow client, int seq,            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,            Rect outContentInsets, InputChannel outInputChannel) {        int[] appOp = new int[1];        //检查Permission是否合法        int res = mPolicy.checkAddPermission(attrs, appOp);        if (res != WindowManagerGlobal.ADD_OKAY) {            return res;        }        boolean reportNewConfig = false;        WindowState attachedWindow = null;        WindowState win = null;        long origId;        final int type = attrs.type;        synchronized(mWindowMap) {            if (!mDisplayReady) {                throw new IllegalStateException("Display has not been initialialized");            }            final DisplayContent displayContent = getDisplayContentLocked(displayId);            if (displayContent == null) {                Slog.w(TAG, "Attempted to add window to a display that does not exist: "                        + displayId + ".  Aborting.");                return WindowManagerGlobal.ADD_INVALID_DISPLAY;            }            if (!displayContent.hasAccess(session.mUid)) {                Slog.w(TAG, "Attempted to add window to a display for which the application "                        + "does not have access: " + displayId + ".  Aborting.");                return WindowManagerGlobal.ADD_INVALID_DISPLAY;            }            if (mWindowMap.containsKey(client.asBinder())) {                Slog.w(TAG, "Window " + client + " is already added");                return WindowManagerGlobal.ADD_DUPLICATE_ADD;            }            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {                attachedWindow = windowForClientLocked(null, attrs.token, false);                if (attachedWindow == null) {                    Slog.w(TAG, "Attempted to add window with token that is not a window: "                          + attrs.token + ".  Aborting.");                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;                }                if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW                        && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {                    Slog.w(TAG, "Attempted to add window with token that is a sub-window: "                            + attrs.token + ".  Aborting.");                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;                }            }            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {                Slog.w(TAG, "Attempted to add private presentation window to a non-private display.  Aborting.");                return WindowManagerGlobal.ADD_PERMISSION_DENIED;            }            boolean addToken = false;            WindowToken token = mTokenMap.get(attrs.token);            if (token == null) {                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {                    Slog.w(TAG, "Attempted to add application window with unknown token "                          + attrs.token + ".  Aborting.");                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }                if (type == TYPE_INPUT_METHOD) {                    Slog.w(TAG, "Attempted to add input method window with unknown token "                          + attrs.token + ".  Aborting.");                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }                if (type == TYPE_VOICE_INTERACTION) {                    Slog.w(TAG, "Attempted to add voice interaction window with unknown token "                          + attrs.token + ".  Aborting.");                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }                if (type == TYPE_WALLPAPER) {                    Slog.w(TAG, "Attempted to add wallpaper window with unknown token "                          + attrs.token + ".  Aborting.");                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }                if (type == TYPE_DREAM) {                    Slog.w(TAG, "Attempted to add Dream window with unknown token "                          + attrs.token + ".  Aborting.");                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }                token = new WindowToken(this, attrs.token, -1, false);                addToken = true;            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {                AppWindowToken atoken = token.appWindowToken;                if (atoken == null) {                    Slog.w(TAG, "Attempted to add window with non-application token "                          + token + ".  Aborting.");                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;                } else if (atoken.removed) {                    Slog.w(TAG, "Attempted to add window with exiting application token "                          + token + ".  Aborting.");                    return WindowManagerGlobal.ADD_APP_EXITING;                }                if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {                    // No need for this guy!                    if (localLOGV) Slog.v(                            TAG, "**** NO NEED TO START: " + attrs.getTitle());                    return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;                }            } else if (type == TYPE_INPUT_METHOD) {                if (token.windowType != TYPE_INPUT_METHOD) {                    Slog.w(TAG, "Attempted to add input method window with bad token "                            + attrs.token + ".  Aborting.");                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }            } else if (type == TYPE_VOICE_INTERACTION) {                if (token.windowType != TYPE_VOICE_INTERACTION) {                    Slog.w(TAG, "Attempted to add voice interaction window with bad token "                            + attrs.token + ".  Aborting.");                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }            } else if (type == TYPE_WALLPAPER) {                if (token.windowType != TYPE_WALLPAPER) {                    Slog.w(TAG, "Attempted to add wallpaper window with bad token "                            + attrs.token + ".  Aborting.");                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }            } else if (type == TYPE_DREAM) {                if (token.windowType != TYPE_DREAM) {                    Slog.w(TAG, "Attempted to add Dream window with bad token "                            + attrs.token + ".  Aborting.");                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                }            } else if (token.appWindowToken != null) {                Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);                // It is not valid to use an app token with other system types; we will                // instead make a new token for it (as if null had been passed in for the token).                attrs.token = null;                token = new WindowToken(this, null, -1, false);                addToken = true;            }            win = new WindowState(this, session, client, token,                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);            if (win.mDeathRecipient == null) {                // Client has apparently died, so there is no reason to                // continue.                Slog.w(TAG, "Adding window client " + client.asBinder()                        + " that is dead, aborting.");                return WindowManagerGlobal.ADD_APP_EXITING;            }            if (win.getDisplayContent() == null) {                Slog.w(TAG, "Adding window to Display that has been removed.");                return WindowManagerGlobal.ADD_INVALID_DISPLAY;            }            mPolicy.adjustWindowParamsLw(win.mAttrs);            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));            res = mPolicy.prepareAddWindowLw(win, attrs);            if (res != WindowManagerGlobal.ADD_OKAY) {                return res;            }            if (outInputChannel != null && (attrs.inputFeatures                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {                String name = win.makeInputChannelName();                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);                win.setInputChannel(inputChannels[0]);                inputChannels[1].transferTo(outInputChannel);                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);            }            // From now on, no exceptions or errors allowed!            res = WindowManagerGlobal.ADD_OKAY;            origId = Binder.clearCallingIdentity();            if (addToken) {                mTokenMap.put(attrs.token, token);            }            win.attach();            mWindowMap.put(client.asBinder(), win);            if (win.mAppOp != AppOpsManager.OP_NONE) {                if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage())                        != AppOpsManager.MODE_ALLOWED) {                    win.setAppOpVisibilityLw(false);                }            }            if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) {                token.appWindowToken.startingWindow = win;                if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken                        + " startingWindow=" + win);            }            boolean imMayMove = true;            if (type == TYPE_INPUT_METHOD) {                win.mGivenInsetsPending = true;                mInputMethodWindow = win;                addInputMethodWindowToListLocked(win);                imMayMove = false;            } else if (type == TYPE_INPUT_METHOD_DIALOG) {                mInputMethodDialogs.add(win);                addWindowToListInOrderLocked(win, true);                moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));                imMayMove = false;            } else {                addWindowToListInOrderLocked(win, true);                if (type == TYPE_WALLPAPER) {                    mLastWallpaperTimeoutTime = 0;                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;                } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;                } else if (mWallpaperTarget != null                        && mWallpaperTarget.mLayer >= win.mBaseLayer) {                    // If there is currently a wallpaper being shown, and                    // the base layer of the new window is below the current                    // layer of the target window, then adjust the wallpaper.                    // This is to avoid a new window being placed between the                    // wallpaper and its target.                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;                }            }            win.mWinAnimator.mEnterAnimationPending = true;            if (displayContent.isDefaultDisplay) {                mPolicy.getContentInsetHintLw(attrs, outContentInsets);            } else {                outContentInsets.setEmpty();            }            if (mInTouchMode) {                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;            }            if (win.mAppToken == null || !win.mAppToken.clientHidden) {                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;            }            mInputMonitor.setUpdateInputWindowsNeededLw();            boolean focusChanged = false;            if (win.canReceiveKeys()) {                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,                        false /*updateInputWindows*/);                if (focusChanged) {                    imMayMove = false;                }            }            if (imMayMove) {                moveInputMethodWindowsIfNeededLocked(false);            }            assignLayersLocked(displayContent.getWindowList());            // Don't do layout here, the window must call            // relayout to be displayed, so we'll do it there.            if (focusChanged) {                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);            }            mInputMonitor.updateInputWindowsLw(false /*force*/);            if (localLOGV) Slog.v(                TAG, "New client " + client.asBinder()                + ": window=" + win);            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {                reportNewConfig = true;            }        }        if (reportNewConfig) {            sendNewConfiguration();        }        Binder.restoreCallingIdentity(origId);        return res;    }

大致的工作有如下几点:检查在xml中是否声明好权限,接着判断是哪个层级的Window,再判断之前在WindowMananger.LayoutParms中的参数,还有对应错误的type等并作出相应的异常处理,添加window到列表中,加入事件的分发机制等等。

Window的删除动作:
同上面的思路·一样是在WindowGlobal的removeview中实现:

public void removeView(View view, boolean immediate) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        synchronized (mLock) {            int index = findViewLocked(view, true);            View curView = mRoots.get(index).getView();            removeViewLocked(index, immediate);            if (curView == view) {                return;            }            throw new IllegalStateException("Calling with view " + view                    + " but the ViewAncestor is attached to " + curView);        }    }     private int findViewLocked(View view, boolean required) {        final int index = mViews.indexOf(view);        if (required && index < 0) {            throw new IllegalArgumentException("View=" + view + " not attached to window manager");        }        return index;    }

这里的逻辑就很清晰,是通过 findViewLocked来查找之前初始化好的mView的list中对应View的索引,再调用removeViewLocked来删除。

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

在这里同样还是通过ViewRootImpl中完成删除的。注意WindowManager中还提供了removeviewimmediate这种同步删除机制, 为了避免出现意外错误请将imm为判断为false。我们看看ViewRootImpl中die方法:

boolean die(boolean immediate) {        // Make sure we do execute immediately if we are in the middle of a traversal or the damage        // done by dispatchDetachedFromWindow will cause havoc on return.        if (immediate && !mIsInTraversal) {            doDie();            return false;        }        if (!mIsDrawing) {            destroyHardwareRenderer();        } else {            Log.e(TAG, "Attempting to destroy the window while drawing!\n" +                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());        }        mHandler.sendEmptyMessage(MSG_DIE);        return true;    }

在die方法中只做了简单的判断,如果是接着通过发送信息给Handler去调用doDie方法,真正的逻辑是在里面的 dispatchDetachedFromWindow();完成的。

void doDie() {        checkThread();        if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);        synchronized (this) {            if (mRemoved) {                return;            }            mRemoved = true;            if (mAdded) {                dispatchDetachedFromWindow();            }            if (mAdded && !mFirst) {                destroyHardwareRenderer();                if (mView != null) {                    int viewVisibility = mView.getVisibility();                    boolean viewVisibilityChanged = mViewVisibility != viewVisibility;                    if (mWindowAttributesChanged || viewVisibilityChanged) {                        // If layout params have been changed, first give them                        // to the window manager to make sure it has the correct                        // animation info.                        try {                            if ((relayoutWindow(mWindowAttributes, viewVisibility, false)                                    & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {                                mWindowSession.finishDrawing(mWindow);                            }                        } catch (RemoteException e) {                        }                    }                    mSurface.release();                }            }            mAdded = false;        }        WindowManagerGlobal.getInstance().doRemoveView(this);    }

上面的方法主要完成的工作如下:垃圾回收,通过Session的Remove来移除Window,调用View的 dispatchDetachedFromWindow();方法,调用 WindowManagerGlobal.getInstance().doRemoveView(this);刷新列表中数据。

Window的更新动作:
我们继续看看WindowManagerGlobal中的update:

 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        if (!(params instanceof WindowManager.LayoutParams)) {            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");        }        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);        }    }

逻辑也很清晰,view调用setLayoutParams去调用ViewRootImpl中requestLayout,通过findViewLocked寻找view对应的索引,刷新view中对应的数据。

在这里可以总结为一句话,就是View是Android中的视图呈现方式,但是它不能单独存在,它必须依附在Window上。

必须一提的是:WindowManager的工作流程不仅仅只有这么一点,从添加Window的过程中我们还发现里面还涉及到了WindowState这个类,我直译为Window的状态,里面保存了Window相关的句柄(WindowToken),对应的WindowManager,Uid,WindowId,显示内容等等,这些参数主要是在ViewRootImpl中的setView方法中参与工作。

0 0
原创粉丝点击