【Android】浮動視窗層級分析
轉載自:http://www.liuguangli.win/archives/category/uncategorized/android
最近在專案中遇到了這樣的需求:需要在特定的其他應用之上懸浮自己的UI互動(拖動、輸入等複雜的UI互動),和九遊的浮窗類似,不過我們的比九遊的體驗更好,我們越過了很多授權的限制。
很多人都知道如何去實現一個簡單的浮窗,但是卻很少有人去深入的研究背後的流程機制,由於專案中浮窗互動比較複雜,遇到了些坑查看了很多資料,故總結浮窗涉及到的知識點:
- 視窗層級關係(浮窗是如何“浮”的)?
- 浮窗有哪些限制,如何越過使用者授權實現浮窗功能?
- 視窗與使用者輸入系統(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),所以要實現浮動視窗我們只能在系統視窗範圍中實現。
型別 | 常量範圍 | 子類 | 常量值 | 說明 | 例子 |
APPLICATION_WINDOW | 1~99 | TYPE_BASE_APPLICATION | 1 | ||
TYPE_APPLICATION | 2 | 應用視窗 | 大部分的應用程式視窗 | ||
TYPE_APPLICATION_STARTING | 3 | 應用程式的Activity顯示之前由系統顯示的視窗 | |||
LAST_APPLICATION_WINDOW | 99 | ||||
SUB_WINDOW | 1000~1999 | FIRST_SUB_WINDOW | 1000 | ||
TYPE_APPLICATION_PANEL | 1000 | 顯示在母視窗之上,遮擋其下面的應用視窗。 | |||
TYPE_APPLICATION_MEDIA | 1001 | 顯示在母視窗之下,如果應用視窗不挖洞,即不可見。 | SurfaceView,在小視窗顯示時設為MEDIA, 全屏顯示時設為PANEL | ||
TYPE_APPLICATION_SUB_PANEL | 1002 | ||||
TYPE_APPLICATION_ATTACHED_DIALOG | 1003 | ||||
TYPE_APPLICATION_MEIDA_OVERLAY | 1004 | 用於兩個SurfaceView的合成,如果設為MEDIA,則上面的SurfaceView 擋住下面的SurfaceView | |||
SYSTEM_WINDOW | 2000~2999 | TYPE_STATUS_BAR | 2000 | 頂部的狀態列 | |
TYPE_SEARCH_BAR | 2001 | 搜尋視窗,系統中只能有一個搜尋視窗 | |||
TYPE_PHONE | 2002 | 電話視窗 | |||
TYPE_SYSTEM_ALERT | 2003 | 警告視窗,在所有其他視窗之上顯示 | 電量不足提醒視窗 | ||
TYPE_KEYGUARD | 2004 | 鎖屏介面 | |||
TYPE_TOAST | 2005 | 短時的文字提醒小視窗 | |||
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 | 輸入法視窗,會擠佔當前應用的空間 | |||
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 | 當前應用視窗,多使用者情況下只顯示在使用者節目 |
到這裡我們對Android系統的視窗層次有個大致的瞭解了,Activity是Android應用的四大元件之一,描述的是應用的活動狀態和週期,受ActivityManagerService的管理;Window/View是圖形視窗的抽象模型,描述的是視窗的繪製資訊,受WindowManagerService的管理;Activity聚合Window來和圖形視窗產生聯絡。文章旨在理解一下Android窗體系統的一個雛形,能力有限不能詳細跟蹤整個視窗體系的原始碼,有興趣的可以自己深入,下一篇文章:《越過授權使用浮窗》。