要點(diǎn)提煉|開發(fā)藝術(shù)之Window

多數(shù)情況我們是和ActivityView打交道,在之前學(xué)習(xí)中也都接觸過,本篇來深入學(xué)習(xí)和它們有緊密聯(lián)系的Window,主要內(nèi)容:

  • Window&WindowManager
  • Window的內(nèi)部機(jī)制(添加、刪除、更新)
  • Window的創(chuàng)建過程(Activity、Dialog、Toast)

1.Window&WindowManager

a.Window&PhoneWindow:
Window是一個(gè)抽象類,它定義了頂級(jí)窗體樣式和行為。其唯一的實(shí)現(xiàn)類是PhoneWindow。

推薦閱讀Window,PhoneWindow,DecorView,setContentView源碼理解

b.Window&View:
每個(gè)Window都對應(yīng)一個(gè)View和一個(gè)ViewRootImpl,Window和View通過ViewRootImpl來建立聯(lián)系。Window并不可見,它實(shí)際以View的形式存在,它是View的直接管理者

c.Window&WindowManager:
實(shí)際使用中無法訪問Window,對Window的訪問必須通過WindowManager,對Window的操作通過它完成。

  • 例如:通過WindowManager添加Window
//將一個(gè)Button添加到屏幕為(100,300)的位置
mFloatingButton = new Button(this);
mFloatingButton.setText("test button");

mLayoutParams = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,PixelFormat.TRANSPARENT);//第三個(gè)參數(shù)代表flags,第四個(gè)參數(shù)代表type

mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
        | LayoutParams.FLAG_NOT_FOCUSABLE
        | LayoutParams.FLAG_SHOW_WHEN_LOCKED;//配置flags
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;//配置type
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;//配置gravity
mLayoutParams.x = 100;//相對于gravity
mLayoutParams.y = 300;//相對于gravity

mFloatingButton.setOnTouchListener(this);
mWindowManager.addView(mFloatingButton, mLayoutParams);

這里依次介紹WindowManager的三個(gè)重要參數(shù):

  • flags:表示W(wǎng)indow的屬性。主要的可選值含義:
    • FLAG_NOT_FOCUSABLE:表示W(wǎng)indow不需要獲取焦點(diǎn),也不需要接收各種輸入事件,此標(biāo)記會(huì)同時(shí)啟動(dòng)FLAG_NOT_TOUCH_MODEL,最終事件會(huì)傳遞給下層的具有焦點(diǎn)的Window。
    • FLAG_NOT_TOUCH_MODAL:表示系統(tǒng)會(huì)將當(dāng)前Window區(qū)域以外的單擊事件傳遞給底層的Window,而區(qū)域以內(nèi)的單擊事件則自己處理。一般都需要開啟此標(biāo)記,否則其他Window將無法收到單擊事件。
    • FLAG_SHOW_WHEN_LOCKED:表示W(wǎng)indow可顯示在鎖屏界面。
  • type:表示W(wǎng)indow的類型。Window有三種類型:
    • 應(yīng)用Window:對應(yīng)一個(gè)Activity。
    • 子Window:不能單獨(dú)存在,需附屬特定的父Window。如Dialog。
    • 系統(tǒng)Window: 需申明權(quán)限才能創(chuàng)建。如Toast。
  • Window是分層的,見下表。
  • 層級(jí)大的會(huì)覆蓋在層級(jí)小的Window上面。
  • 對應(yīng)WindowManager.LayoutParams的type參數(shù)。
  • gravity:表示W(wǎng)indow的位置。
    • 默認(rèn)是屏幕中間。
    • x、y值相對于gravity。

d.WindowManager&WindowManagerService:
Window的具體實(shí)現(xiàn)位于WindowManagerService中。WindowManager和WindowManagerService的交互是一個(gè)IPC(跨進(jìn)程通信)過程。


2.Window的內(nèi)部機(jī)制

  • WindowManager對Window主要有三大操作:添加、更新和刪除。這三個(gè)方法主要是定義在ViewManager接口中:
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);//添加過程
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);//更新過程
    public void removeView(View view);//刪除過程
}
  • WindowManager也是一個(gè)接口,它繼承了ViewManager接口:
public interface WindowManager extends ViewManager {}
  • WindowManager的具體實(shí)現(xiàn)類是WindowManagerImpl
public final class WindowManagerImpl implements WindowManager{
        @Override
        public void addView(View view, ViewGroup.LayoutParams params){
            mGlobal.addView(view, params, mDisplay, mParentWindow);
        }
        
        @Override
        public void updateViewLayout(View view, ViewGroup.LayoutParams params){
            mGlobal.updateViewLayout(view, params);
        }
        
        @Override
        public void removeView(View view){
            mGlobal.removeView(view, false);
        }
}
  • 由以上代碼可見,WindowManagerImpl并沒有直接實(shí)現(xiàn)Window的三大操作,而是交給了WindowManagerGlobal。WindowManagerGlobal以單例模式向外提供自己的實(shí)例:
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

一幅圖說明這幾個(gè)類的關(guān)系:

因此,通過WindowManagerGlobal的addView()、updateViewLayout()、removeView()實(shí)現(xiàn)WindowManager對Window的添加、刪除和修改。

下面分別來看WindowManagerGlobal對Window操作的大致過程:

a.Window的添加過程:

b.Window的刪除過程

c.Window的更新過程

不難發(fā)現(xiàn), 以上驗(yàn)證了之前的總結(jié):

  • Windows的三大操作最終都會(huì)通過一個(gè)IPC過程移交給WindowManagerService。
  • Window和View通過ViewRootImpl來聯(lián)系,ViewRootImpl可控制View的測量、布局和重繪。

推薦閱讀:源碼剖析之------Window的內(nèi)部實(shí)現(xiàn)機(jī)制、我眼中的Window創(chuàng)建/添加/刪除/更新過程


3.Window的創(chuàng)建過程

由于View必須依附Window才能呈現(xiàn)出來,因此有View的地方必有Window。在Android中可以提供View的地方有Activity、Dialog和Toast,下面分別來看以上三種Window的大致創(chuàng)建過程:

a.Activity的Window創(chuàng)建過程

推薦閱讀Activity的Window創(chuàng)建過程分析(源碼)

b.Dialog的Window創(chuàng)建過程

Step1:創(chuàng)建WindowDialog。和Activity類似,同樣是通過PolicyManager.makeNewWindow()來實(shí)現(xiàn)。

Step2:初始化DecorView并將Dialog的視圖添加到DecorView中去。和Activity類似,同樣是通過Window.setContentView()來實(shí)現(xiàn)。

Step3:將DecorView添加到Window中顯示。和Activity一樣,都是在自身要出現(xiàn)在前臺(tái)時(shí)才會(huì)將添加Window。

  • Dialog.show()方法:完成DecorView的顯示。
  • WindowManager.remoteViewImmediate()方法:當(dāng)Dialog被dismiss時(shí)移除DecorView。

c.Toast的Window創(chuàng)建過程

①Toast的內(nèi)部的視圖由兩種方式指定:

  • 系統(tǒng)默認(rèn)的樣式;
  • 通過setView()指定一個(gè)自定義View。

這里見技能篇之Toast的使用

②Toast具有定時(shí)取消功能,故系統(tǒng)采用Handler做定時(shí)處理。

③在Toast內(nèi)部有兩類IPC過程:

  • Toast訪問NotificationManagerService(NMS);
  • NotificationManagerService回調(diào)Toast里的TN接口。

④Toast提供方法show()cancel()分別用于顯示和隱藏Toast。

  • Toast的顯示和隱藏都需要通過NMS來實(shí)現(xiàn),由于NMS運(yùn)行在系統(tǒng)進(jìn)程中,故需通過遠(yuǎn)程調(diào)用的方式來進(jìn)行顯示和隱藏Toast。
  • NMS處理Toast的顯示和隱藏請求時(shí)會(huì)跨進(jìn)程回調(diào)TN中的方法,由于TN運(yùn)行在Binder線程池中,故需通過Handler將其切換到當(dāng)前線程(發(fā)送Toast請求的線程)。

NMS只是起到了管理Toast隊(duì)列及其延時(shí)的效果,Toast 的顯示和隱藏實(shí)際是通過TN來實(shí)現(xiàn)的。

推薦閱讀:Android對話框Dialog,PopupWindow,Toast的實(shí)現(xiàn)機(jī)制


希望這篇文章對你有幫助~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容