1.視窗層級關系(浮窗是如何“浮”的)?
2.浮窗有哪些限制,如何越過使用者授權實作浮窗功能?
3.視窗與使用者輸入系統(Activity是如何接收到touch事件?)。
第一個問題:浮窗為何會浮。 浮窗之是以叫浮窗,是因為它能懸浮于應用或者桌面視窗之上,能脫離Activity而存在。為了研究其中差別,我們先來看看我們最熟悉的Activity是怎麼顯示出來的。
Activity是怎麼顯示出來的?
要弄清這個問題答案,我們先從Activity的setContentView()這個方法的源碼開始找起,在Activity中看到setCententView的源碼:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow是傳回傳回Activity的mWindow變量,指向一個Window的對象,Window是一個抽象類,這裡傳回的是PhoneWindow對象(PhoneWindow是Window的子類),PhoneWindow中有一個DecorView對象,decorView成員,這是一個FrameLayout,setContentView的子布局最終會添加到decorView中,這個decorView就是目前視窗的根視圖,這個根視圖是如何最終被繪制出來的?在ActivityThread中有這樣一段代碼:
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
這個decorView,最終會被WindowManager.addView添加到繪制系統中,并類型是WindowManager.LayoutParams.TYPE_BASE_APPLICATION,這個參數決定了要繪制的視窗的z軸層次,為了避免思維棧過深,這裡就不貼出詳細的源碼跟蹤過程了,直接給結論。
先來看看Activity和window的關系:

再來window和View的關系:
Activity視窗顯示過程:
說Activity是怎麼顯示出來的,其實是說Activity管理的View是怎麼顯示出來的。最後再來總結一下:
一、Activity通過setContentView設定的視圖是添加到PhoneWindow的根視圖decor中。
二、Window是一個抽象的概念,Window關了了一個View(根視圖),最終被WindowManager管理的還是一個View(根視圖)和它的LayoutParams,視圖繪制重新整理都是通過WindowManager(WindowManagerGlobal)與WindowManagerServiceIPC互動調用底層繪制的。
三、Activity是四大元件中唯一和窗體緊密聯系的元件(這是為什麼會有初學者把Activity直接了解為繪制界面的原因),所有掌管的視圖隻不過是一種window和Dialog、Toast、牆紙所掌管的Window類型不一樣。
浮窗為什麼會“浮”?
上面講到Activity的顯示過程其實已經揭示了通用界面的顯示過程,浮窗的顯示過程更為簡單:
做過浮窗的同學應該都明白了,為啥浮窗能脫離Activity而顯示,本質上我們是把一個View交給WindowManager來管理了,LayoutParams.type類型決定了這個View顯示視窗的類型,不同類型顯示的視窗層次(z軸)是不一樣的。大方面來講可以分為應用視窗(APPLICATION_WINDOW)、子視窗(SUB_WINDOW)、系統視窗(SYSTEM_WINDOW)三種類型,應用視窗z軸範圍是1~99,子視窗的範圍是1001~1999,系統視窗是(2000~2999),是以要實作浮動視窗我們隻能在系統視窗範圍中實作。
更新SDK 29
類型 | 常量範圍 | 子類 | 常量值 | 說明 | 例子 |
---|---|---|---|---|---|
APPLICATION_WINDOW | 1~99 | 應用視窗 | |||
FIRST_APPLICATION_WINDOW | 1 | 應用程式視窗。 | |||
TYPE_BASE_APPLICATION | 1 | 所有程式視窗的“基地”視窗,其他應用程式視窗都顯示在它上面 | |||
TYPE_APPLICATION | 2 | 普通應用功能程式視窗。token必須設定為Activity的token,以指出該視窗屬誰。 | 大部分的應用程式視窗 | ||
TYPE_APPLICATION_STARTING | 3 | 應用程式的Activity顯示之前由系統顯示的視窗 | 用于應用程式啟動時所顯示的視窗。應用本身不要使用這種類型。它用于讓系統顯示些資訊,直到應用程式可以開啟自己的視窗 | ||
TYPE_DRAWN_APPLICATION | 4 | TYPE_APPLICATION的一種變體,可確定視窗*管理器在顯示應用程式之前将等待此視窗被繪制。僅在擁有使用者的視窗上顯示 | |||
LAST_APPLICATION_WINDOW | 99 | 應用程式視窗結束 | |||
SUB_WINDOW | 1000~1999 | 子視窗 | |||
FIRST_SUB_WINDOW | 1000 | ||||
TYPE_APPLICATION_PANEL | 1000 | 面闆視窗,顯示于宿主視窗上層,遮擋其下面的應用視窗。 | 子視窗。子視窗的Z序和坐标空間都依賴于他們的宿主視窗。 | ||
TYPE_APPLICATION_MEDIA | 1001 | 媒體視窗,例如視訊。顯示于宿主視窗下層,如果應用視窗不挖洞,即不可見。SurfaceView,在小視窗顯示時設為MEDIA, 全屏顯示時設為PANEL | |||
TYPE_APPLICATION_SUB_PANEL | 1002 | 應用程式視窗的子面闆。顯示于所有面闆視窗的上層。(GUI的一般規律,越“子”越靠上) | |||
TYPE_APPLICATION_ATTACHED_DIALOG | 1003 | 對話框。類似于面闆視窗,繪制類似于頂層視窗,而不是宿主的子視窗。 | |||
TYPE_APPLICATION_MEIDA_OVERLAY | 1004 | 用于兩個SurfaceView的合成,如果設為MEDIA,則上面的SurfaceView 擋住下面的SurfaceView | 媒體資訊。顯示在媒體層和程式視窗之間,需要實作透明(半透明)效果。(例如顯示字幕) | ||
TYPE_APPLICATION_ABOVE_SUB_PANEL | 1005 | 位于應用程式視窗頂部的子面闆視窗。這些視窗顯示在其附加視窗TYPE_APPLICATION_SUB_PANEL面闆的頂部 | |||
LAST_SUB_WINDOW | 1999 | 子視窗結束。( End of types of sub-windows ) | |||
SYSTEM_WINDOW | 2000~2999 | 系統視窗 | |||
FIRST_SYSTEM_WINDOW | 2000 | 系統視窗。非應用程式建立。 | |||
TYPE_STATUS_BAR | 2000 | 狀态欄。隻能有一個狀态欄;它位于螢幕頂端,其他視窗都位于它下方。 | 頂部的狀态欄 | ||
TYPE_SEARCH_BAR | 2001 | 搜尋欄。隻能有一個搜尋欄;它位于螢幕上方 | |||
TYPE_PHONE | 2002 | 電話視窗。它用于電話互動(特别是呼入)。它置于所有應用程式之上,狀态欄之下 | 電話視窗 | ||
TYPE_SYSTEM_ALERT | 2003 | 系統提示。它總是出現在應用程式視窗之上。 | 警告視窗,在所有其他視窗之上顯示 電量不足提醒視窗 | ||
TYPE_KEYGUARD | 2004 | 鎖屏界面 | |||
TYPE_TOAST | 2005 | 短時的文字提醒小視窗 | 資訊視窗。用于顯示toast。 | ||
TYPE_SYSTEM_OVERLAY | 2006 | 系統頂層視窗。顯示在其他一切内容之上。此視窗不能獲得輸入焦點,否則影響鎖屏。 | 沒有焦點的浮動視窗 | ||
TYPE_PRIORITY_PHONE | 2007 | 電話優先,當鎖屏時顯示。此視窗不能獲得輸入焦點,否則影響鎖屏。 | 緊急電話視窗,可以顯示在屏保之上 | ||
TYPE_SYSTEM_DIALOG | 2008 | 系統資訊彈出視窗對話框。 | 例如音量調節框、SIM插上後彈出的營運商資訊視窗 | ||
TYPE_KEYGUARD_DIALOG | 2009 | 跟KeyGuard綁定的彈出對話框 鎖屏時的滑動解鎖視窗 | 鎖屏時顯示的對話框。 | ||
TYPE_SYSTEM_ERROR | 2010 | 系統錯誤提示視窗,顯示于所有内容之上。 | ANR 視窗 | ||
TYPE_INPUT_METHOD | 2011 | 内部輸入法視窗,顯示于普通UI之上。應用程式可重新布局以免被此視窗覆寫。 | 輸入法視窗,會擠占目前應用的空間 | ||
TYPE_INPUT_METHOD_DIALOG | 2012 | 彈出的輸入法視窗,不會擠占目前應用視窗空間,在其之上顯示 | |||
TYPE_WALLPAPER | 2013 | 牆紙 | |||
TYPE_STATUS_BAR_PANEL | 2014 | 從狀态條下拉的視窗 | 狀态欄滑動面闆 | ||
TYPE_SECURE_SYSTEM_OVERLAY | 2015 | 隻有系統使用者可以建立的OVERLAY視窗 | |||
TYPE_DRAG | 2016 | 浮動的可拖動視窗 | 360安全衛士的浮動精靈 | ||
TYPE_STATUS_BAR_PANEL 2017 | |||||
TYPE_POINTER | 2018 | 光标 | |||
TYPE_NAVIGATION_BAR | 2019 | ||||
TYPE_VOLUME_OVERLAY | 2020 | 音量調節視窗 | |||
TYPE_BOOT_PROGRESS | 2021 | 啟動進度,在所有視窗之上 | |||
TYPE_HIDDEN_NAV_CONSUMER | 2022 | 隐藏的導航欄 | |||
TYPE_DREAM | 2023 | 屏保動畫 | |||
TYPE_NAVIGATION_BAR_PANEL | 2024 | Navigation bar 彈出的視窗 比如說應用收集欄 | |||
TYPE_UNIVERSAL_BACKGROUND | 2025 | ||||
TYPE_DISPLAY_OVERLAY | 2026 | 用于模拟第二顯示裝置 | |||
TYPE_MAGNIFICATION | 2027 | 用于放大局部 | |||
TYPE_RECENTS_OVERLAY | 2028 | 目前應用視窗,多使用者情況下隻顯示在使用者節目 | |||
TYPE_PRIVATE_PRESENTATION | 2030 | 在專用虛拟顯示器上的示範視窗 | |||
TYPE_VOICE_INTERACTION | 2031 | 語音互動層中的Windows | |||
TYPE_ACCESSIBILITY_OVERLAY | 2032 | 無障礙使用的 | 如果存在可觸摸的全屏可通路性覆寫,則可通路性服務将自檢其下方的視窗即使*被可觸摸視窗覆寫。 | ||
TYPE_VOICE_INTERACTION_STARTING | 2033 | 語音互動層的啟動視窗 | |||
TYPE_DOCK_DIVIDER | 2034 | 顯示用于調整停靠堆棧大小的句柄的視窗。該視窗由系統程序擁 | |||
TYPE_QS_DIALOG | 2035 | 類似于TYPE_APPLICATION_ATTACHED_DIALOG但由快速設定圖塊使用 | |||
TYPE_SCREENSHOT | 2036 | 與TYPE_DREAM具有相似的特征,保留該層以供選擇螢幕快照區域。這些視窗不能占據輸入焦點 | |||
TYPE_PRESENTATION | 2037 | 外部顯示器上的示範視窗 | |||
TYPE_APPLICATION_OVERLAY | 2038 | 應用程式覆寫視窗顯示在所有活動視窗上方。在FIRST_APPLICATION_WINDOW和LAST_APPLICATION_WINDOW之間的類型,但在關鍵系統視窗(例如狀态欄或IME)下方。系統可以随時更改這些視窗的位置,大小或可見性以減少使用者的視覺混亂并管理資源。需android.Manifest.permission.SYSTEM_ALERT_WINDOW}權限。系統将調整此視窗類型的程序的重要性,以減少低記憶體殺手殺死它們的機會。僅在擁有使用者的螢幕上顯示 | |||
LAST_SYSTEM_WINDOW | 2999 | 系統視窗結束。 |