Window和WindowManager(二)
Window的刪除過程
上篇文章我們講到了Window的新增過程,接下來我們來分析Window的刪除過程。還是和新增一樣,首先通過WindowManagerImpl這個物件呼叫removeView方法。
@Override public void removeView(View view) { mGlobal.removeView(view, false); }
接著通過 WindowManagerGlobal mGlobal 物件呼叫removeView方法。我們來看WindowManagerGlobal 中的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); } }
我們來看核心實現,首先從mViews集合獲取需要刪除View的索引,因為在新增View的時候,把View新增到了mViews這個集合。
int index = findViewLocked(view, true); 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; }
通過索引呼叫removeViewLocked方法,刪除該View
removeViewLocked(index, immediate); 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); } } }
我們看上面的原始碼。首先通過該View對應的索引,獲取對應的ViewRootImpl類,我們知道每個Window和View多是通過ViewRootImpl建立連線的,對Window的操作,其實就是通過ViewRootImpl來操作的。每個Window都會有對應的ViewRootImpl類。拿到該View對應的ViewRootImpl也就是該Window對應的ViewRootImpl類之後,呼叫die方法進行刪除操作。
boolean deferred = root.die(immediate);
我們來看下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; }
在這個方法中,有一個immediate引數,表示非同步刪除還是同步刪除,如果是同步刪除,直接呼叫doDie()方法。如果是非同步刪除,傳送一個訊息 mHandler.sendEmptyMessage(MSG_DIE);然後呼叫doDiew()方法。因此,刪除的核心方法都在doDie()中。我們來看下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(); } 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); }
在doDie()方法中,刪除的核心實現通過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(); }
在dispatchDetachedFromWindow方法中主要完成以下這些操作。
(1)呼叫View的dispatchDetachedFromWindow();方法,做一些資源回收的工作,比如終止動畫,停止執行緒等。
(2)垃圾回收,比如清楚資料和訊息,移除回撥
(3)第三點比較重要,也是實現刪除Window的真正實現, mWindowSession.remove(mWindow);在上篇文章我們知道,mWindowSession是一個Binder物件,這裡是一個IPC機制,通過跨程序通訊,把刪除Window的操作交給了WindowManagerService進行刪除。
(4)呼叫WindowManagerGlobal.getInstance().doRemoveView(this);把當前儲存的刪除的View 列表進行移除重新整理。
以上就是Window的刪除大概的流程,我們做下小結:
我們知道刪除Window
1.首先交給WindowManagerGlobal 的removie方法,這個方法是為了拿到需要刪除的View的索引,再通過該索引拿到對應的ViewRootImpl物件,
2.然後刪除Window的實現交給了ViewRootImpl來完成。通過呼叫die方法。
3.最後的核心刪除操作就是和新增Window類似,通過Binde物件mWindowSession實現跨程序通訊,把最終的刪除操作交給了WindowManagerService完成,刪除完成之後,再對一些垃圾的回收,資料的清除,回撥的移除,以及刪除列表的更新。
Window的更新過程
我們上面分別介紹了Window的新增以及刪除過程,下面我們來介紹,Window的更新過程。
還是一樣首先從WindowImpl的updateViewLayout
@Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); }