WMS(二):Window的刪除過程

作者:劉望舒
鏈接:http://www.reibang.com/p/f9b3787fc0cd

前言

在本系列文章中,我提到過:Window的操作分為兩大部分苫费,一部分是WindowManager處理部分,另一部分是WMS處理部分,Window的刪除過程也不例外研侣,本篇文章會介紹Window的刪除過程喻括,包括了兩大處理部分的內(nèi)容。

Window的刪除過程

Android解析WindowManagerService(二)WMS的重要成員和Window的添加過程這篇文章中Window的創(chuàng)建和更新過程類似领追,要刪除Window需要先調(diào)用WindowManagerImpl的removeView方法他膳,removeView方法中又會調(diào)用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處調(diào)用了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處根據(jù)傳入的索引在ViewRootImpl列表中獲得V的ViewRootImpl肢预。注釋2處得到InputMethodManager實例,如果InputMethodManager實例不為null則在注釋3處調(diào)用InputMethodManager的windowDismissed方法來結(jié)束V的輸入法相關(guān)的邏輯洼哎。注釋4處調(diào)用ViewRootImpl 的die方法烫映,如下所示。

frameworks/base/core/java/android/view/ViewRootImpl.java

  boolean die(boolean immediate) {
        //die方法需要立即執(zhí)行并且此時ViewRootImpl不在執(zhí)行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(需要立即執(zhí)行)谱净,并且mIsInTraversal值為false則執(zhí)行注釋2處的代碼秽梅,mIsInTraversal在執(zhí)行ViewRootImpl的performTraversals方法時會被設(shè)置為true坦喘,在performTraversals方法執(zhí)行完時被設(shè)置為false,因此注釋1處可以理解為die方法需要立即執(zhí)行并且此時ViewRootImpl不在執(zhí)行performTraversals方法。注釋2處的doDie方法如下所示密似。
frameworks/base/core/java/android/view/ViewRootImpl.java

void doDie() {
    //檢查執(zhí)行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處用于檢查執(zhí)行doDie方法的線程的正確性谜叹,注釋1的內(nèi)部會判斷執(zhí)行doDie方法線程是否是創(chuàng)建V的原始線程藤抡,如果不是就會拋出異常纺座,這是因為只有創(chuàng)建V的原始線程才能夠操作V。注釋2到注釋3處的代碼用于防止doDie方法被重復(fù)調(diào)用导盅。注釋4處V有子View就會調(diào)用dispatchDetachedFromWindow方法來銷毀View较幌。注釋6處如果V有子View并且不是第一次被添加,就會執(zhí)行后面的代碼邏輯白翻。注釋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操作相關(guān)的三個列表,doRemoveView方法會從這三個列表中清除V對應(yīng)的元素岛琼。注釋1處找到V對應(yīng)的ViewRootImpl在ViewRootImpl列表中的索引底循,接著根據(jù)這個索引從ViewRootImpl列表、布局參數(shù)列表和View列表中刪除與V對應(yīng)的元素槐瑞。
我們接著回到ViewRootImpl的doDie方法熙涤,查看注釋5處的dispatchDetachedFromWindow方法里做了什么:
frameworks/base/core/java/android/view/ViewRootImpl.java

  void dispatchDetachedFromWindow() {
      ...
        try {
            mWindowSession.remove(mWindow);
        } catch (RemoteException e) {
        }
        ...
    }

dispatchDetachedFromWindow方法中主要調(diào)用了IWindowSession的remove方法,IWindowSession在Server端的實現(xiàn)為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對應(yīng)的WindowState,WindowState用于保存窗口的信息悼沿,在WMS中它用來描述一個窗口等舔。接著在注釋2處調(diào)用WindowState的removeIfPossible方法,如下所示显沈。
frameworks/base/services/core/java/com/android/server/wm/WindowState.java

Override
void removeIfPossible() {
    super.removeIfPossible();
    removeIfPossible(false /*keepVisibleDeadWindow*/);
}

又會調(diào)用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方法和它的名字一樣鳖藕,并不是直接執(zhí)行刪除操作魔慷,而是進行多個條件判斷過濾,滿足其中一個條件就會return著恩,推遲刪除操作院尔。比如這時V正在運行一個動畫,這時就得推遲刪除操作喉誊,直到動畫完成邀摆。通過這些條件判斷過濾就會執(zhí)行注釋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意味著正在執(zhí)行刪除Window操作栋盹,注釋1到注釋2處之間的代碼用于防止重復(fù)刪除操作。注釋3處如果當前要刪除的Window是StatusBar或者NavigationBar就會將這個Window從對應(yīng)的控制器中刪除敷矫。注釋4處會將V對應(yīng)的Session從WMS的ArraySet<Session> mSessions中刪除并清除Session對應(yīng)的SurfaceSession資源(SurfaceSession是SurfaceFlinger的一個連接例获,通過這個連接可以創(chuàng)建1個或者多個Surface并渲染到屏幕上 )。注釋5處調(diào)用了WMS的postWindowRemoveCleanupLocked方法用于對V進行一些集中的清理工作曹仗,這里就不在繼續(xù)深挖下去榨汤,有興趣的同學(xué)可以自行查看源碼。

Window的刪除過程就講到這里怎茫,雖然刪除的操作邏輯比較復(fù)雜收壕,但是可以簡單的總結(jié)為以下4點:

  1. 檢查刪除線程的正確性,如果不正確就拋出異常。
  2. 從ViewRootImpl列表蜜宪、布局參數(shù)列表和View列表中刪除與V對應(yīng)的元素旬渠。
  3. 判斷是否可以直接執(zhí)行刪除操作,如果不能就推遲刪除操作端壳。
  4. 執(zhí)行刪除操作告丢,清理和釋放與V相關(guān)的一切資源。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末损谦,一起剝皮案震驚了整個濱河市岖免,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌照捡,老刑警劉巖颅湘,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異栗精,居然都是意外死亡闯参,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門悲立,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹿寨,“玉大人,你說我怎么就攤上這事薪夕〗挪荩” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵原献,是天一觀的道長馏慨。 經(jīng)常有香客問我,道長姑隅,這世上最難降的妖魔是什么写隶? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮讲仰,結(jié)果婚禮上慕趴,老公的妹妹穿的比我還像新娘。我一直安慰自己叮盘,他們只是感情好秩贰,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著柔吼,像睡著了一般毒费。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愈魏,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天觅玻,我揣著相機與錄音想际,去河邊找鬼。 笑死溪厘,一個胖子當著我的面吹牛胡本,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播畸悬,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼侧甫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蹋宦?” 一聲冷哼從身側(cè)響起披粟,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冷冗,沒想到半個月后守屉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蒿辙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年拇泛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片思灌。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡俺叭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出习瑰,到底是詐尸還是另有隱情绪颖,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布甜奄,位于F島的核電站,受9級特大地震影響窃款,放射性物質(zhì)發(fā)生泄漏课兄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一晨继、第九天 我趴在偏房一處隱蔽的房頂上張望烟阐。 院中可真熱鬧,春花似錦紊扬、人聲如沸蜒茄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽檀葛。三九已至,卻和暖如春腹缩,著一層夾襖步出監(jiān)牢的瞬間屿聋,已是汗流浹背空扎。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留润讥,地道東北人转锈。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像楚殿,于是被迫代替她去往敵國和親撮慨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容