Dialog dismiss 流程

  1. Dialog.dismiss():
    • 如果是在非UI線程調用,會將dismiss這個指令schedule到UI線程的handler來異步執行
    • 否則同步執行。
  2. Dialog.dismissDialog()。
  3. WindowManagerImpl.removeViewImmediate(mDecor), mDecor在Dialog show()的時候調用mWindow.getDecorView()獲得。
  4. WindowManagerGlobal.removeView(view, true): WindowManagerGlobal是進程級的單例,本進程內所有Window相關信息進行集中管理的結點,也包括了ViewRootImpl(Window的View樹根節點)
    • 根據傳入的view通過findViewLocked()在mViews**(維護了所有當前Window的DecorView)**中找到其所屬Window對應的index.
  5. WindowManagerGlobal.removeViewLocked(int index, boolean immediate):
    • 根據index獲得Window的ViewRootImpl.
    • 回調本進程的InputMethodManager的windowDismissed(mViews.get(index).getWindowToken())來告知輸入法窗口某個Window即將dismiss,從而使得輸入法窗口消失(finishInputLocked(), 如果當前輸入法的servedView屬於該Window的話)。
    • 調用ViewRootImpl的die(immediate), 如果返回true, 代表die()沒有被立刻執行,而是schedule(defer)到將來。
      • 如果defer, 那麼將此View加入到mDyingViews來保持追蹤。
  6. ViewRootImpl.die(boolean immediate):
    • 如果需要立刻執行(immediate == true)或者當前沒有在進行Traversal(ViewRootImpl的一個操作,可以簡單理解爲對整個View樹的measure+layout+draw)中,那麼可以立即開始(doDie()), 進而返回false代表操作沒有被defer.
    • 否則schedule一個MSG_DIE來defer die()。
  7. ViewRootImpl.doDie():
    • 如果已經dead(mRemoved == true), 不需要再死一次。
    • 標記爲dead(mRemoved = true)
    • 如果該View之前被添加到了WMS中(mAdded = true), 調用dispatchDetachedFromWindow()來分發detach。
      • dispatchDetachedFromWindow():
        • 會回調其AttachInfo的ViewTreeObserver的dispatchOnWindowAttachedChange(false)
        • 會回調ViewRootImpl承載的View(mView)的dispatchDetachedFromWindow將detach的消息分發到整個View tree。
        • mView(DecorView)的parent設爲null(assignParent(null))
        • mView = null, 釋放DecorView。
        • 釋放Surface(mSurface = null)。
        • 調用mWindowSession.remove(mWindow)來將ViewRootImpl對應的Window從WMS中移除
        • 釋放InputChannel,不再接收input信息。
        • unscheduleTraversals()取消之前schedule的Traversal任務(窗口都消失了,不再需要)。
    • 如果添加到了WMS,但是還從來沒有進行過Traversal(mFirst == false)
      • invalidateDisplayLists()
      • destroyHardwareRenderer()
      • 下面的邏輯會判斷mView != null, 不過在4.4的源碼中,很奇怪,因爲如果mAdded, 那麼dispatchDetachedFromWindow()必然會將mView設置爲null, 而這個判斷進行的前提也是mAdded,即在進入這個判斷中時,mView就一定是null了,mView != null的邏輯似乎永遠不會進入到,因此這段邏輯不分析。
    • 重置mAdded = false。
    • WindowManagerGlobal.getInstance().doRemoveView(this), 告知WindowManagerGlobal該ViewRootImpl已經完成了自己的remove相關操作(WMS那邊也已經知曉並開始remove了),後者會徹底消除ViewRootImpl在其內部的相關信息:包括在mRoots, mParams,mDyingViews中的信息.
    • 至此,Window在本地Window體系中的信息基本都被清除了。
  8. Session: Session是每個ViewRootImpl和WMS通信的RPC節點
    • remove(IWindow window)最終會RPC調到WMS的removeWindow(this, window)
  9. WMS.removeWindow(Session session, IWindow client):
    • 通過windowForClientLocked(session, client, false)獲取到Window對應的WindowState(win, 等於是該Window在WMS體系中的代理人和信息保存者).
    • 爲了多線程安全,會synchronized mWindowMap這個保存所有WindowState的map再向下執行。
    • removeWindowLocked(session, win).
  10. WMS.removeWindowLocked(Session session, WindowState win)
    • win.disposeInputChannel(), 註銷對應的mInputChannel,不再接收input消息。
    • 如果window之前是可見的,並且已經爲其分配了Surface:
      • 會安排一個合適的結束動畫(WindowManagerPolicy.TRANSIT_EXIT/TRANSIT_PREVIEW_DONE)
      • 使用window的WindowStateAnimator來驅動此動畫(applyAnimationLocked().
      • 如果確實發起了結束動畫, 那麼會將winState的mExiting設置爲true.
        • 並且設置win.mRemoveOnExit = true, win.mDisplayContent.layoutNeeded = true
        • performLayoutAndPlaceSurfacesLocked()來開始動畫。
        • 結束函數,真正的remove會在動畫結束以後進行。
    • removeWindowInnerLocked(session, win):
    • 如果之前window是可見的並且其消失會導致Configuration的變化(比如橫屏變豎屏),那麼會schedule一個H.SEND_NEW_CONFIGURATION來處理新的Configuration.
    • updateFocusedWindowLocked, 讓新的window獲取焦點。
  11. WMS.removeWindowInnerLocked(Session session, WindowState win):

    • 遍歷Window的ChildWindow,遞歸的對每個childWindow進行removeWindowInnerLocked.
    • 如果當前輸入法的target是此Window,那麼顯然需要爲輸入法找到下一個合適的target(moveInputMethodWindowsIfNeededLocked(false))
    • 將window從PhoneWindowManager中移除(mPolicy.removeWindowLw(win))
    • 調用WindowState的removeLocked()來讓其完成自己內部的資源釋放清理。
    • 將相關信息從mWindowMap移除。
    • windows/mPendingRemove/mResizingWindows中都移除此Window的相關信息。
    • 釋放window的相關token, (WindowToken, AppWindowToken)
    • 如果當前WMS並不在layout過程中(mInLayout = false):
      • assignLayersLocked(windows)根據最新的windows信息(刪除了此window的)來重新爲window分配layer。
      • win.mDisplayContent.layoutNeeded = true, 表明此window所屬的DispalyContent需要進行layout.
      • performLayoutAndPlaceSurfacesLocked() 發起一次layout.
  12. 簡單的總結下:

    • Dialog的dismiss本質是其Window從WMS中被remove。
    • Dialog的Window remove 流程和Activity的一致
    • 某種意義上講,Window體系有兩套,本地(WindowManagerGloabl + WindowManagerImpl)和遠端(Session + WMS)
    • 本地Window體系負責維護本進程(一般是一個App)所有Window相關信息
    • WMS則是整個系統的Window管理者和真正的操作執行者
    • Window的remove要先在本地(WindowManagerGlobal以及WindowManagerImlp這一套本地的Window機制)執行/登記,然後再到遠端(WMS,本地的remove觸發了Session向遠端發起請求)執行
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章