相關文章
Android系統啟動系列
Android深入四大元件系列
Android應用程式啟動過程系列
Android解析WindowManager系列
前言
在本系列文章中,我提到過:Window的操作分為兩大部分,一部分是WindowManager處理部分,另一部分是WMS處理部分,Window的刪除過程也不例外,本篇文章會介紹Window的刪除過程,包括了兩大處理部分的內容。
Window的刪除過程
和Android解析WindowManagerService(二)WMS的重要成員和Window的新增過程這篇文章中Window的建立和更新過程類似,要刪除Window需要先呼叫WindowManagerImpl的removeView方法,removeView方法中又會呼叫WindowManagerGlobal的removeView方法,我們就從這裡開始講起。為了表述的更易於理解,本文將要刪除的Window(View)簡稱為V。WindowManagerGlobal的removeView方法如下所示。
frameworks/base/core/java/android/view/WindowManagerGlobal.java
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);//1
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);//2
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
複製程式碼
註釋1處找到要V在View列表中的索引,在註釋2處呼叫了removeViewLocked方法並將這個索引傳進去,如下所示。 frameworks/base/core/java/android/view/WindowManagerGlobal.java
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);//1
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();//2
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());//3
}
}
boolean deferred = root.die(immediate);//4
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
複製程式碼
註釋1處根據傳入的索引在ViewRootImpl列表中獲得V的ViewRootImpl。註釋2處得到InputMethodManager例項,如果InputMethodManager例項不為null則在註釋3處呼叫InputMethodManager的windowDismissed方法來結束V的輸入法相關的邏輯。註釋4處呼叫ViewRootImpl 的die方法,如下所示。
frameworks/base/core/java/android/view/ViewRootImpl.java
boolean die(boolean immediate) {
//die方法需要立即執行並且此時ViewRootImpl不在執行performTraversals方法
if (immediate && !mIsInTraversal) {//1
doDie();//2
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;
}
複製程式碼
註釋1處如果immediate為ture(需要立即執行),並且mIsInTraversal值為false則執行註釋2處的程式碼,mIsInTraversal在執行ViewRootImpl的performTraversals方法時會被設定為true,在performTraversals方法執行完時被設定為false,因此註釋1處可以理解為die方法需要立即執行並且此時ViewRootImpl不在執行performTraversals方法。註釋2處的doDie方法如下所示。
frameworks/base/core/java/android/view/ViewRootImpl.java
void doDie() {
//檢查執行doDie方法的執行緒的正確性
checkThread();//1
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {//2
return;
}
mRemoved = true;//3
if (mAdded) {//4
dispatchDetachedFromWindow();//5
}
if (mAdded && !mFirst) {//6
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);//7
}
複製程式碼
註釋1處用於檢查執行doDie方法的執行緒的正確性,註釋1的內部會判斷執行doDie方法執行緒是否是建立V的原始執行緒,如果不是就會丟擲異常,這是因為只有建立V的原始執行緒才能夠操作V。註釋2到註釋3處的程式碼用於防止doDie方法被重複呼叫。註釋4處V有子View就會呼叫dispatchDetachedFromWindow方法來銷燬View。註釋6處如果V有子View並且不是第一次被新增,就會執行後面的程式碼邏輯。註釋7處的WindowManagerGlobal的doRemoveView方法,如下所示。 frameworks/base/core/java/android/view/WindowManagerGlobal.java
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root);//1
if (index >= 0) {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
}
複製程式碼
WindowManagerGlobal中維護了和 Window操作相關的三個列表,doRemoveView方法會從這三個列表中清除V對應的元素。註釋1處找到V對應的ViewRootImpl在ViewRootImpl列表中的索引,接著根據這個索引從ViewRootImpl列表、佈局引數列表和View列表中刪除與V對應的元素。 我們接著回到ViewRootImpl的doDie方法,檢視註釋5處的dispatchDetachedFromWindow方法裡做了什麼: frameworks/base/core/java/android/view/ViewRootImpl.java
void dispatchDetachedFromWindow() {
...
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
...
}
複製程式碼
dispatchDetachedFromWindow方法中主要呼叫了IWindowSession的remove方法,IWindowSession在Server端的實現為Session,Session的remove方法如下所示。 frameworks/base/services/core/java/com/android/server/wm/Session.java
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
複製程式碼
接著檢視WMS的removeWindow方法: frameworks/base/services/core/java/com/android/server/wm/WindowManagerService .java
void removeWindow(Session session, IWindow client) {
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);//1
if (win == null) {
return;
}
win.removeIfPossible();//2
}
}
複製程式碼
註釋1處用於獲取Window對應的WindowState,WindowState用於儲存視窗的資訊,在WMS中它用來描述一個視窗。接著在註釋2處呼叫WindowState的removeIfPossible方法,如下所示。 frameworks/base/services/core/java/com/android/server/wm/WindowState.java
@Override
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
}
複製程式碼
又會呼叫removeIfPossible方法,如下所示。 frameworks/base/services/core/java/com/android/server/wm/WindowState.java
private void removeIfPossible(boolean keepVisibleDeadWindow) {
...條件判斷過濾,滿足其中一個條件就會return,推遲刪除操作
removeImmediately();//1
if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
}
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
Binder.restoreCallingIdentity(origId);
}
複製程式碼
removeIfPossible方法和它的名字一樣,並不是直接執行刪除操作,而是進行多個條件判斷過濾,滿足其中一個條件就會return,推遲刪除操作。比如這時V正在執行一個動畫,這時就得推遲刪除操作,直到動畫完成。通過這些條件判斷過濾就會執行註釋1處的removeImmediately方法: frameworks/base/services/core/java/com/android/server/wm/WindowState.java
@Override
void removeImmediately() {
super.removeImmediately();
if (mRemoved) {//1
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
"WS.removeImmediately: " + this + " Already removed...");
return;
}
mRemoved = true;//2
...
mPolicy.removeWindowLw(this);//3
disposeInputChannel();
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
mSession.windowRemovedLocked();//4
try {
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
} catch (RuntimeException e) {
}
mService.postWindowRemoveCleanupLocked(this);//5
}
複製程式碼
removeImmediately方法如同它的名字一樣,用於立即進行刪除操作。註釋1處的mRemoved為true意味著正在執行刪除Window操作,註釋1到註釋2處之間的程式碼用於防止重複刪除操作。註釋3處如果當前要刪除的Window是StatusBar或者NavigationBar就會將這個Window從對應的控制器中刪除。註釋4處會將V對應的Session從WMS的ArraySet<Session> mSessions
中刪除並清除Session對應的SurfaceSession資源(SurfaceSession是SurfaceFlinger的一個連線,通過這個連線可以建立1個或者多個Surface並渲染到螢幕上 )。註釋5處呼叫了WMS的postWindowRemoveCleanupLocked方法用於對V進行一些集中的清理工作,這裡就不在繼續深挖下去,有興趣的同學可以自行檢視原始碼。
Window的刪除過程就講到這裡,雖然刪除的操作邏輯比較複雜,但是可以簡單的總結為以下4點:
- 檢查刪除執行緒的正確性,如果不正確就丟擲異常。
- 從ViewRootImpl列表、佈局引數列表和View列表中刪除與V對應的元素。
- 判斷是否可以直接執行刪除操作,如果不能就推遲刪除操作。
- 執行刪除操作,清理和釋放與V相關的一切資源。