注:本文解析的源码基于 API 25,部分内容来自于《Android开发艺术探索》。
这篇将是 Window 系列的最后一篇了,主要来讲讲 Window 删除的机制原理。
其实相对于 Window 的添加和更新来说,删除也是换汤不换药的。也是通过 WindowSession 和 WindowManagerService 来完成这个步骤的。
Window的删除机制 我们删除 Window 的代码:
WindowManager.removeView
WindowManagerImpl removeView(View view) 1 2 3 4 @Override public void removeView (View view) { mGlobal.removeView(view, false ); }
WindowManager 是一个接口,具体实现是 WindowManagerImpl 类。不用说,WindowManagerImpl 内部肯定是 WindowManagerGlobal 在“作祟”咯。
WindowManagerGlobal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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, boolean immediate)
先找到了打算删除的 View 的索引。然后根据索引去执行删除操作。
若 immediate
参数传入的是 true ,那么就执行了同步删除操作;否则就是异步删除操作了。大多使用的都是异步删除操作,避免出错,即 immediate
为 false;
其实这个方法的重点都放在了 removeViewLocked(index, immediate)
中了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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); } } }
在 removeViewLocked(int index, boolean immediate)
中,调用了 ViewRootImpl 的 die 方法。大多数的默认情况下,immediate
都为 false 。
之后又将 view 添加到 mDyingViews 中。mDyingViews 维持着都是即将要删除的 View 。
ViewRootImpl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 boolean die (boolean immediate) { 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 ; }
在 die(boolean immediate)
方法中,不管同步还是异步,都是执行 doDie()
方法。不同的就是同步是马上执行,而异步是利用 Handler 去发消息,接收到消息后在执行。
doDie() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void doDie () { checkThread(); if (LOCAL_LOGV) Log.v(mTag, "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) { 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 ); }
doDie()
方法中主要看两点:
dispatchDetachedFromWindow() 是去执行删除 window 的方法;
WindowManagerGlobal.getInstance().doRemoveView(this) 把 mRoot 、mParams 和 mDyingViews 中关于当前 Window 的参数都移除了。
所以我们接下来,还是要看下 dispatchDetachedFromWindow() 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 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) { } if (mInputChannel != null ) { mInputChannel.dispose(); mInputChannel = null ; } mDisplayManager.unregisterDisplayListener(mDisplayListener); unscheduleTraversals(); }
在方法一开头,先回调了 View 的 dispatchDetachedFromWindow 方法,该方法表示 View 马上要从 Window 上删除了。在这个方法内,可以做一些资源回收的工作。
之后做的就是一些垃圾回收的工作,比如清楚数据和消息,移除回调等。
再然后要看的就是 mWindowSession.remove(mWindow)
,这步才是真正调用了 Session 来移除 Window 的操作,是 IPC 的过程。具体的我们深入去看了。
Session 1 2 3 public void remove (IWindow window) { mService.removeWindow(this , window); }
在 Session 中直接调用了 WindowManagerService 的 removeWindow(Session session, IWindow client)
方法。
WindowManagerService 1 2 3 4 5 6 7 8 9 10 11 public void removeWindow (Session session, IWindow client) { synchronized (mWindowMap) { WindowState win = windowForClientLocked(session, client, false ); if (win == null ) { return ; } removeWindowLocked(win); } }
先得到 WindowState 对象,再调用 removeWindowLocked 去移除该 WindowState 。而具体的 removeWindowLocked 代码我们在这就不深入了,可以自行研究。
至此,整个 Window 移除机制就分析完毕了。