天天看點

Android 開發藝術探索筆記(17)

Window的删除過程

Window的删除過程和添加差不多,也是實作WindowManagerImpl後,進一步橋接WindowManagerGlobal來實作。下面是WindowManagerGlobal的removeView實作:

Android 開發藝術探索筆記(17)

這個邏輯比較清晰,就是用findViewLocked去查找View的索引,然後通過removeViewLocked去删除這個View。這個方法裡通過ViewRootImpl來進行删除。

在WindowManager中提供了兩種删除接口removeView、removeViewImmediate,它們分别表示異步删除和同步删除。一般我們都用異步方法,具體的删除時用到ViewRootImpl的die方法來。在異步删除的情況下,die方法隻是發送了一個請求删除的操作的消息後立刻傳回,這個View并沒有完成删除操作,是以會添加到mDyingViews中,mDyingViews表示待删除的View清單。然後在die裡面呢,如果是異步,就用發送消息,handler處理并調用doDie方法,而同步不會發送消息直接調用doDie。在doDie内部會調用dispatchDetachedFromWindow方法,這是真正删除Window的邏輯。它主要做四件事:

  • 垃圾回收的相關工作,比如清楚資料
  • 調用onDeatchFromWindow方法,在這個方法裡結束程序、終止動畫等
  • 通過Session的remove方法删除Winodow,最終會調用WindowManagerService的removeWindow方法,這是個IPC過程
  • 調用doRemoveView方法重新整理資料。

Window更新

跟之前兩個一樣,也是看WindowManagerGlobal的updateViewLayout方法。

就是用新的View的LayoutParams替換老的,接着再去更新ViewRootImpl中的LayoutParams。通過ViewRootImpl中的ScheduleTraversals方法對View進行重布局,除了本身的重繪以外,還回去通過Session來更新Window視圖,最終也是到了WindowManagerService中,同樣是IPC過程。

Window的建立

一共分為三個部分,因為Window一共有三中: 應用類Window(Activity)、子Window和系統Window。詳細過程比較複雜,這裡先做大概的了解。

Activity的建立

首先要分析Activity的啟動過程,Activity的啟動過程很複雜,最終會由ActivityThread中的PerformLaunchActivity()來完成,在這個方法的内部會通過類加載器建立Activity的執行個體對象,并調用其attach方法為其關聯運作過程中所依賴的一系列上下文環境變量。代碼如下:

Android 開發藝術探索筆記(17)

在Activity的attach方法裡,系統會建立Activity所屬的Window對象并為其回調接口,Window對象的建立是通過PolicyManager的makeNewWindow方法實作的。由于Activity實作了Window的Callback接口,是以當Window接收到外界的狀态改變時就會回調Activity方法。Callback接口中方法就很多,但是有幾個确實我們非常熟悉的,比如onAttachedToWindow、onDetachedFromWindow、dispatchTouchEvent等等。代碼如下:

Android 開發藝術探索筆記(17)

可以看出Activity的Window是通過PolicyManager的一個工廠方法來建立的,PolicyManager實作的工廠方法全部在政策接口IPolicy:

Android 開發藝術探索筆記(17)

這裡的makeNewWindow傳回的是一個PhoneWindow對象。到這裡Activity的建立就完成了。

接着就是分析Activity怎麼顯示在視圖上的,我們通過setContentView來看,它裡面就是調用了Window即剛剛PhoneWindow的方法,它的方法大概如下:

  • 如果沒有DecorView,則建立它

    DecorView是一個FrameLayout,是頂級的Layout,包含一個标題欄和内容欄。DecorView的建立由installDecor完成,在方法内部通過generateDecor方法來直接建立DecorView,這個時候DecorView還隻是一個空白的FrameLayout。

  • Android 開發藝術探索筆記(17)
  • 為了初始化DecorView結構,PhoneWindow還需要通過generateLayout來加載具體的布局到DecorView中,具體的布局檔案和系統版本以及主題有關,這個過程如下:
  • Android 開發藝術探索筆記(17)
  • 将View添加到DecorView的mContentParent中

    直接将Activity視圖添加到DecorView的mContentParent:mLayoutInflater.inflater(layoutResID,mContentParent)。到此為止,Activity的布局檔案已經添加到DecorView中了,

  • 回調onContentChanged方法通知Activity視圖已改變

    Activity中的onContentChanged方法是一個空實作,可以在子Activity中處理這個回調。

這個時候已經将DecorView添加到mContentParent中了,但是還沒有正式的被WindowManager添加到Window中,是以它還無法從外界接收資訊。在ActivityThread的handleResumeActivity方法中,首先會調用Activity的onResume方法,接着會調用Activity的makeVisable(),正式在makeVisable中,DecorView才真正地完成了添加和顯示這兩個過程,到這裡Activity才能被看到。

Dialog的建立

跟Activity的建立差不多,也是用PolicyManager傳回的PhoneWindow來建立。

接着初始化DecorView并将Dialog添加到DecorView中。

最後将DecorView添加到Window中并顯示。

當其關閉時通過WindowManager來移除DecorView:mWindowManager.removeViewImmediate(mDecor)

普通Dialog有一個特殊之處,那就是必須采用Activity的Context,如果采用Application的Context,那就會報錯,是因為沒有應用token,而token隻有Activity才有,是以隻需要Activity作為Context來顯示對話框。而系統Window就比較特殊,它不用使用token,是以當你給dialog設定層級,讓它的層級和系統Window一樣,它就可以不用Activity作為Context來顯示。

Toast的建立

Toast是基于Window實作的,但是它有定時取消這一功能,是以它用到了Handler。Toast内部有兩類IPC過程,一類是Toast通路NotificationManagerService,第二類是NotificationManagerService(NMS)回調Toast裡的TN接口。

Toast是系統Window,它内部的視圖有兩種方式指定,一種是系統預設的樣式,另一種是通過setView方式來自定義一個View,不管如何,它們都對應Toast的一個View類型的内部成員mNextView。Toast提供了show和cancel分别用于顯示和隐藏Toast。它們内部是一個IPC過程。代碼如下:

Android 開發藝術探索筆記(17)

無論顯示還是隐藏都要用到NMS,由于NMS運作在系統中,是以隻能通過遠端調用來實作show和cancel方法。TN是一個Binder類,它申請之後,NMS會回調,這個時候TN是線上程池中的,是以需要通過Hnadler将其切換到目前線程中。因為這裡使用了Handler,是以Toast無法再沒有Looper的線程中彈出。

NMS的enqueueToast三個參數分别為目前包名,回調的TN,持續的時間。enqueueToast首先将Toast請求封裝為ToastRecord對象并為其添加到一個名為mToastQueue隊列中,然後排隊處理。然後NMS會通過showNextToastRecord方法來顯示目前的Toast,Toast就是通過Callback完成的。最終回調TN的方法将Toast顯示。

顯示Toast之後,NMS還會通過scheduleTimeoutLocked方法來發送一個延遲資訊,延遲時間為Toast的顯示時長:

Android 開發藝術探索筆記(17)

繼續閱讀