Android 源码解析之WindowManager删除窗口

来源:互联网 发布:贪吃蛇java小游戏代码 编辑:程序博客网 时间:2024/05/21 19:29

一,写在前面  

        在博客Android 源码解析之WindowManager添加窗口 中,先是介绍了如何通过WindowManager添加一个系统窗口,并从源码角度分析了PhoneWindow的创建时机,Window中创建WindowManagerImpl对象,以及创建ViewRootImpl对象,并调用setView方法完成添加窗口的操作。在ViewRootImpl类内部,通过一次IPC调用(基于Binder机制)和系统服务WindowManagerService进行通信,获取到Session对象。finally,在Session类的内部,将添加窗口的操作交给WindowManagerService处理。

二,进入主题

       本篇文章是基于窗口的添加基础上进行介绍,强烈建议阅读本篇文章的哥们,先了解WindowManager是如何添加窗口,见Android 源码解析之WindowManager添加窗口 。因此,直接从WindomManagerImpl开始分析,WindomManagerImpl有这样两个方法都可以删除窗口。
       源码如下:
    @Override    public void removeView(View view) {        mGlobal.removeView(view, false);    }    @Override    public void removeViewImmediate(View view) {        mGlobal.removeView(view, true);    }
       可以看出两个方法都调用了WindomManagerGlobal的removeView方法,只是第二个参数的boolean值不同,调用removeViewImmediate时传入true,调用removeView传入false。
       
       查看WindomManagerGlobal$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);        }    }
       removeView最开始对参数view进行判空检查,为空则抛出异常。为避免多线程同时删除一个View,在同步代码块中处理删除窗口的操作,继续分析代码块中内容。
       其中,WindomManagerGlobal$findViewLocked源码如下:
    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;    }
       在文章Android 源码解析之WindowManager添加窗口介绍过,mViews是一个存放View的List集合,添加窗口时,会将View添加到mViews中。首先调用list.indexOf获取该View的索引位置,若没有找到该View则抛出异常。在实际开发中,如果该View没有添加到窗口中,那么删除该View会报错。可以在执行删除操作前进行判断,避免抛出异常。也即是说,findViewLocked方法是获取到被删除的View对象在集合中的索引。

       回到WindomManagerGlobal$removeView方法,内部的调用了removeViewLocked方法。
       查看WindomManagerGlobal$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);            }        }    }
       第2行,通过索引位置index,获取list集合中ViewRootImpl对象。前篇文章中有讲过,在添加窗口的流程中,每添加一次窗口,就会创建一个ViewRootImpl方法,并添加到集合mRoots中。
       第3行,调用ViewRootImpl类的方法,获取被删除的View对象。
       第11行,真正删除窗口的操作是交给ViewRootImpl$die方法。
       ViewRootImpl$die源码如下:
    /**     * @param immediate True, do now if not in traversal. False, put on queue and do later.     * @return True, request has been queued. False, request has been completed.     */    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(mTag, "Attempting to destroy the window while drawing!\n" +                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());        }        mHandler.sendEmptyMessage(MSG_DIE);        return true;    }
       第8行,若immediate为true,也就是调用removeViewImmediate删除窗口,直接调用doDie(),并返回false;
       第19行,若immediate为false,也就是调用removeView删除窗口,则使用Handler发送一个消息,并返回true;消息的处理也是调用doDie(),只是多了一步,通过消息机制来处理。
       消息处理源码如下:
    final class ViewRootHandler extends Handler {//...@Override        public void handleMessage(Message msg) {//...    case MSG_DIE:doDie();break;//...}//...        }

       回到WindomManagerGlobal$removeViewLocked方法,第14行,deferred为true时,也就是调用removeView删除窗口时,将该View对象添加到集合mDyingViews中。注意此时,只是执行了mHandler.sendEmptyMessage(MSG_DIE),发送了一个消息,还并未调用doDie()。因此,mDyingViews存放的是需要删除的View,但仍未真正删除的View。

       通过前面的分析可知:不管使用哪种方式删除窗口,区别在于是否通过消息机制来删除,但最终都是调用了doDie()来处理。
       ViewRootImpl$doDie源码如下:
    void doDie() {        checkThread();        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);        synchronized (this) {            if (mRemoved) {                return;            }            mRemoved = true;            if (mAdded) {                dispatchDetachedFromWindow();            }//...code            mAdded = false;        }        WindowManagerGlobal.getInstance().doRemoveView(this);    }
       第10行,删除窗口操作放在 dispatchDetachedFromWindow方法中;
       第17行,在完成删除窗口操作后,更新WindowManagerGlobal中维护的四个集合的数据。
       查看WindowManagerGlobal$doRemoveView源码:
    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);            }        }        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {            doTrimForeground();        }    }

       回到ViewRootImpl$doDie方法,内部调用dispatchDetachedFromWindow删除窗口。
       查看ViewRootImpl$dispatchDetachedFromWindow源码如下:
    void dispatchDetachedFromWindow() {        if (mView != null && mView.mAttachInfo != null) {            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);            mView.dispatchDetachedFromWindow();        }        mAccessibilityInteractionConnectionManager.ensureNoConnection();        mAccessibilityManager.removeAccessibilityStateChangeListener(                mAccessibilityInteractionConnectionManager);        mAccessibilityManager.removeHighTextContrastStateChangeListener(                mHighContrastTextManager);        removeSendWindowContentChangedCallback();        destroyHardwareRenderer();        setAccessibilityFocus(null, null);        mView.assignParent(null);        mView = null;        mAttachInfo.mRootView = null;        mSurface.release();        if (mInputQueueCallback != null && mInputQueue != null) {            mInputQueueCallback.onInputQueueDestroyed(mInputQueue);            mInputQueue.dispose();            mInputQueueCallback = null;            mInputQueue = null;        }        if (mInputEventReceiver != null) {            mInputEventReceiver.dispose();            mInputEventReceiver = null;        }        try {            mWindowSession.remove(mWindow);        } catch (RemoteException e) {        }        // Dispose the input channel after removing the window so the Window Manager        // doesn't interpret the input channel being closed as an abnormal termination.        if (mInputChannel != null) {            mInputChannel.dispose();            mInputChannel = null;        }        mDisplayManager.unregisterDisplayListener(mDisplayListener);        unscheduleTraversals();    }
       首先是做一些垃圾回收的工作,执行View的一些回调方法,注销了一些接口。
       重点注意第35行,内部执行mWindowSession.remove(mWindow)删除窗口,这里的变量mWindowSession就是Session对象。变量mWindowSession的实现类的获取:内部基于Binder机制,与系统服务WindowManagerService进行通信,获取到Session对象。详情可以查看文章Android 源码解析之WindowManager添加窗口,这里不再阐述。

       查看Session$remove源码如下:
    @Override    public void remove(IWindow window) {        mService.removeWindow(this, window);    }
       mService就是WindowManagerService对象,因此,删除窗口的操作最终是交给WindowManagerService处理了。本篇文章主要介绍WindowManager删除窗口的内部实现原理,分析到这里就结束了。暂不对WindowManagerService删除窗口的具体流程进行介绍,有需要的哥们可以查阅老罗的博客解惑吧~

三,总结

       值得一提的是,WindowManager删除窗口这种说法,内部其实是删除View。每一个View都需要依赖于Window,View不能独立的存在的去添加,删除,更新。窗口Window是一种抽象的概念,抽取出了一些方法,例如,可以创建以及获取WindowManagerImpl的实例。因此,通过WindowManager增加,删除,更新窗口,内部其实是对View的增加,删除,更新。从WindowManagerImpl方法的命名就可以看出,均跟view相关。

       本篇文章介绍了WindowManager删除窗口的的内部实现原理,WindowManagerImpl有removeView,removeViewImmediate两种方式删除窗口。区别在于removeView通过消息机制,在处理的消息时才调用doDie(),removeViewImmediate则是直接调用doDie()。diDie()内部调用dispatchDetachedFromWindow方法,在dispatchDetachedFromWindow方法中调用Session对象的remove方法,内部是将删除窗口的操作交给WindowManagerService处理。

       另外,在文章Android 源码解析之WindowManager添加窗口中介绍了窗口得添加,调用了Session对象的addToDisplay方法,内部也是将添加窗口的操作交给WindowManagerService来处理。由此可见,通过WindowManager删除,添加窗口的操作,最终都是交给系统服务WindowManagerService来处理了。可以猜测更新窗口的操作也是这样的,当然确实如此啦~

 











原创粉丝点击