Android解析WindowManagerService(三)Window的刪除過程

相關文章
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點:
1. 檢查刪除線程的正確性,如果不正確就拋出異常。
2. 從ViewRootImpl列表、佈局參數列表和View列表中刪除與V對應的元素。
3. 判斷是否可以直接執行刪除操作,如果不能就推遲刪除操作。
4. 執行刪除操作,清理和釋放與V相關的一切資源。

公衆號末尾1.2.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章