進(jìn)階之路 | 奇妙的Window之旅

前言

本文已經(jīng)收錄到我的Github個人博客,歡迎大佬們光臨寒舍:

我的GIthub博客

本文已授權(quán)公眾號郭霖涛舍,秦子帥

學(xué)習(xí)清單:

  • Window&WindowManagerService

  • Window&WindowManager

  • Window&PhoneWindow

  • Window&Activity

  • Window&View

  • Window內(nèi)部機制

  • Window創(chuàng)建過程

一.為什么要學(xué)習(xí)Window?

Android手機上所有的視圖都是通過Window來呈現(xiàn)的抖拴,像常用的Activity绵跷,Dialog硝桩,PopupWindow盒齿,Toast念逞,他們的視圖都是附加在Window上的,所以可以這么說 ——「Window是View的直接管理者」边翁。

Window是一個頂層窗口查看和行為的一個抽象基類翎承,這個類的實例作為一個頂級View添加到Window Manager。它提供了一套標(biāo)準(zhǔn)的UI方法符匾,比如添加背景叨咖,標(biāo)題等等。

Window本身很抽象啊胶,深入了解Window甸各,不僅有助于你了解Android系統(tǒng)中各個層級之間的關(guān)系,還可以對Toast的內(nèi)部機制焰坪、自定義等等方面會有更加深入的體會趣倾。

Window

注意:StatusBar也包含在DecorView之內(nèi)

二.核心知識點歸納

2.1 Window關(guān)系解析

看到下面這張大圖,是不是感覺有點亂亂的琳彩,別急誊酌,別急,心急吃不了熱豆腐露乏,筆者將向您娓娓道來

Window整體關(guān)系圖

2.1.1 Window&PhoneWindow

筆者之前在進(jìn)階之路 | 奇妙的View之旅中碧浊,提及setContentView的時候簡單說到了WindowPhoneWindow,相信看過的讀者已經(jīng)對此有一個簡單的印象瘟仿。

Window是一個抽象類箱锐,它定義了頂級窗體樣式和行為。其唯一的實現(xiàn)類PhoneWindow劳较。

Activity的構(gòu)成

2.1.2 Window&View

筆者之前在進(jìn)階之路 | 奇妙的View之旅中驹止,提及View工作流程的時候簡單說到了ViewRootImpl,相信看過的讀者已經(jīng)對此有一個簡單的印象观蜗。

每個Window都對應(yīng)一個View和一個ViewRootImpl臊恋,WindowView通過ViewRootImpl來建立聯(lián)系。Window不可見墓捻,它實際以View的形式存在抖仅,它是View的直接管理者

2.1.3 Window&WindowManagerService

想了解IPC的讀者砖第,可以看下筆者寫的一篇博客: 進(jìn)階之路 | 奇妙的 IPC 之旅

Window具體實現(xiàn)位于WindowManagerService中撤卢。WindowManagerWindowManagerService的交互是一個IPC(跨進(jìn)程通信)過程。

2.1.4 Window&WindowManager

實際使用中無法訪問Window梧兼,對Window的訪問必須通過WindowManager(換句話說放吩,WindowManager是外界訪問Window的入口),對Window的操作通過它完成羽杰。

  • 例如:通過WindowManager添加Window
//將一個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);//第三個參數(shù)代表flags渡紫,第四個參數(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的三個重要參數(shù):

  • flags:表示Window的屬性。主要的可選值含義:
  • FLAG_NOT_FOCUSABLE:表示Window不需要獲取焦點考赛,也不需要接收各種輸入事件腻惠,此標(biāo)記會同時啟動FLAG_NOT_TOUCH_MODEL,最終事件會傳遞給下層的具有焦點的Window
  • FLAG_NOT_TOUCH_MODAL:表示系統(tǒng)會將當(dāng)前Window區(qū)域以外的單擊事件傳遞給底層的Window欲虚,而區(qū)域以內(nèi)的單擊事件則自己處理集灌。一般都需要開啟此標(biāo)記,否則其他Window將無法收到單擊事件
  • FLAG_SHOW_WHEN_LOCKED:表示Window可顯示在鎖屏界面
  • type:表示Window的類型复哆。

    Window有三種類型:

    A.應(yīng)用類Window:對應(yīng)一個Activity或者Dialog

    B.Window:不能單獨存在欣喧,需附屬特定的父Window。如PopupWindow,ContextMenu,OptionMenu

    注意:ContextMenu,OptionMenuDialog的子類梯找,它們修改了自身的窗口類型

    C.系統(tǒng)Window: 需聲明權(quán)限才能創(chuàng)建唆阿。如Toast

    • 系統(tǒng)權(quán)限有很多值,一般選用:TYPE_SYSTEM_OVERLAY/TYPE_SYSTEM_ERROR

    • 記得聲明權(quán)限:< uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>,Android6.0以下直接聲明權(quán)限即可锈锤,Android6.0以上還需要用戶打開軟件設(shè)置頁手動打開驯鳖,才能授權(quán)闲询。

  • Window是分層的,見下表
  • 層級大的會覆蓋在層級小的Window上面浅辙。
  • 對應(yīng)WindowManager.LayoutParamstype參數(shù)扭弧。
Window 層級
應(yīng)用Window 1-99
Window 1000-1999
系統(tǒng)Window 2000-2999
  • gravity:表示Window的位置。
  • 默認(rèn)是屏幕中間
  • x记舆、y值相對于gravity

2.2 Window的內(nèi)部機制

  • WindowManagerWindow主要有三大操作:添加鸽捻、更新和刪除

    這三個方法主要是定義在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也是一個接口泽腮,它繼承了ViewManager接口:
public interface WindowManager extends ViewManager {}
  • WindowManager的具體實現(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并沒有直接實現(xiàn)Window的三大操作,而是交給了WindowManagerGlobal诊赊。WindowManagerGlobal單例模式向外提供自己的實例:

WindowManagerImpl這種工作模式是典型的橋接模式

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

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

類的關(guān)系

因此厚满,通過WindowManagerGlobaladdView()updateViewLayout()碧磅、removeView()實現(xiàn)WindowManagerWindow的添加痰滋、刪除和修改。

2.2.1 Window的添加

Window添加流程

2.2.2 Window的刪除

Window刪除流程

2.2.3 Window的更新

Window的更新流程

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

  • Window的三大操作最終都會通過一個IPC過程移交給WindowManagerService敲街。
  • WindowView通過ViewRootImpl來聯(lián)系,ViewRootImpl可控制View的測量严望、布局和重繪多艇。

限于篇幅,筆者這里暫未貼上源碼像吻,如果想了解的話峻黍,推薦一篇文章:我眼中的Window創(chuàng)建/添加/刪除/更新過程

2.3 Window的創(chuàng)建過程

由于View必須依附Window才能呈現(xiàn)出來,因此有View的地方必有Window拨匆。在Android中可以提供View的地方有Activity姆涩、DialogToastPopupWindow,菜單惭每,下面分別來看Activity骨饿、DialogToast三種Window的大致創(chuàng)建過程

2.3.1 ActivityWindow創(chuàng)建過程

想詳細(xì)了解Activity的啟動流程的,推薦一篇筆者寫的文章:進(jìn)階之路 | 奇妙的四大組件之旅

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

想了解ActivityWindow創(chuàng)建過程的源碼的讀者台腥,筆者推薦一篇文章: Activity的Window創(chuàng)建過程分析

2.3.2 DialogWindow創(chuàng)建過程

Dialog的Window創(chuàng)建過程
  • Dialog.show():完成DecorView的顯示
  • WindowManager.remoteViewImmediate()方法:當(dāng)Dialogdismiss時移除DecorView

2.3.3 ToastWindow創(chuàng)建過程

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

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

Q2:Toast具有定時取消功能宏赘,故系統(tǒng)采用Handler做定時處理

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

  • Toast訪問NotificationManagerService(NotificationManagerService運行在系統(tǒng)的進(jìn)程);
  • NotificationManagerService回調(diào)Toast里的TN接口(運行在Binder線程池)黎侈。

Q4:Toast提供方法show()cancel()分別用于顯示和隱藏Toast察署。

  • Toast的顯示和隱藏都需要通過NMS來實現(xiàn),由于NMS運行在系統(tǒng)進(jìn)程中峻汉,故需通過遠(yuǎn)程調(diào)用的方式來進(jìn)行顯示和隱藏Toast贴汪。
  • NMS處理Toast的顯示和隱藏請求時會跨進(jìn)程回調(diào)TN中的方法脐往,但是由于TN運行在Binder線程池中,故需通過Handler將其切換到當(dāng)前線程(發(fā)送Toast請求的線程)扳埂。
  • NMS只是起到了管理Toast隊列及其延時的效果
  • Toast 的顯示和隱藏實際是通過TN來實現(xiàn)的业簿。
Toast的Window創(chuàng)建流程

想了解ToastWindow創(chuàng)建過程的源碼的讀者,筆者推薦一篇文章:Android對話框Dialog聂喇,PopupWindow辖源,Toast的實現(xiàn)機制

三.課堂小測試

恭喜你蔚携!已經(jīng)看完了前面的文章希太,相信你對Window已經(jīng)有一定深度的了解,下面酝蜒,進(jìn)行一下課堂小測試誊辉,驗證一下自己的學(xué)習(xí)成果吧!

Q1:一個應(yīng)用中有多少個Window?

答案:無限個亡脑。原因:任何一個View都是依附在Window上面堕澄,一個應(yīng)用可以有無限個View,自然Window也是無限個霉咨。

Q2:Window對象有存在的必要嗎蛙紫?

  • 疑惑點:Window能做的事情,View對象基本都能做:像觸摸事件途戒、管理各個子View等等坑傅。
  • 可能有人會說:“WindowView的管理者∨缯”
  • 追問:我們知道唁毒,WindowManagerWindow的管理者,那為什么不直接用WindowManager管理View呢星爪?
  • 答案:站在系統(tǒng)的角度上看浆西,系統(tǒng)是“不知道”有View對象這個說法的!作為系統(tǒng)顽腾,我有自己的驕傲近零,不去管你Window如何搬磚、如何砌墻抄肖,只給你地皮秒赤。而這時,Window為了繪制出用戶想要的組件(按鈕憎瘸、文字入篮、輸入框等等),系統(tǒng)又不給我幌甘!沒事潮售,那我自己定義痊项,于是就定義了View機制,給每個View提供Canvas酥诽,讓不同的View自己繪制具有自己特色的組件鞍泉。同時,為了更好的管理View肮帐,通過定義ViewGroup咖驮,等等。

Q3:Activity有存在的必要嗎训枢?

  • 疑惑點:Window已經(jīng)是系統(tǒng)管理的窗口界面托修。那么為什么還需要Activity呢?我們把Activity所做的事情恒界,全部封裝到Window不就好了睦刃?懸浮窗口Dialog中不就是沒有使用Activity來顯示一個懸浮窗嗎?
  • 答案:Android中的應(yīng)用中十酣,里面對各個窗口的管理相當(dāng)復(fù)雜(任務(wù)棧涩拙、狀態(tài)等等)。但是如果讓用戶自己去管理這些Window耸采,先不說工作量兴泥,光讓用戶自己去實現(xiàn)任務(wù)棧這點,就很難了虾宇。為了讓大家能簡單搓彻、快速的開發(fā)應(yīng)用,AndroidActivity幫我們管理好文留,我們只需簡單的去重寫幾個回調(diào)函數(shù)好唯,無需直接與Window對象接觸。

任何事物都有規(guī)律燥翅,語言再難骑篙,也是人發(fā)明的,一樣具有社會性森书,其實這幾個的關(guān)系就像是國家的中央系統(tǒng)的官員分配一樣靶端,從古至今,一層對一層負(fù)責(zé)凛膏,這樣各司其職杨名,又相互一層層聯(lián)系著,達(dá)到效率最大化猖毫,突然發(fā)現(xiàn)台谍,古人的智慧還是很厲害的,你讓皇帝(系統(tǒng))去管轄所有的官員(view)吁断,豈不是要累死趁蕊?所以才出現(xiàn)了中間這些官員(window


如果文章對您有一點幫助的話坞生,希望您能點一下贊,您的點贊掷伙,是我前進(jìn)的動力

本文參考鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宙地,隨后出現(xiàn)的幾起案子摔认,更是在濱河造成了極大的恐慌,老刑警劉巖绸栅,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件级野,死亡現(xiàn)場離奇詭異页屠,居然都是意外死亡粹胯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門辰企,熙熙樓的掌柜王于貴愁眉苦臉地迎上來风纠,“玉大人,你說我怎么就攤上這事牢贸≈窆郏” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵潜索,是天一觀的道長臭增。 經(jīng)常有香客問我,道長竹习,這世上最難降的妖魔是什么誊抛? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮整陌,結(jié)果婚禮上拗窃,老公的妹妹穿的比我還像新娘。我一直安慰自己泌辫,他們只是感情好随夸,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著震放,像睡著了一般宾毒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上殿遂,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天诈铛,我揣著相機與錄音邪锌,去河邊找鬼。 笑死癌瘾,一個胖子當(dāng)著我的面吹牛觅丰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妨退,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妇萄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了咬荷?” 一聲冷哼從身側(cè)響起冠句,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎幸乒,沒想到半個月后懦底,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡罕扎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年聚唐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腔召。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡杆查,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出臀蛛,到底是詐尸還是另有隱情亲桦,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布浊仆,位于F島的核電站客峭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏抡柿。R本人自食惡果不足惜舔琅,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沙绝。 院中可真熱鬧搏明,春花似錦、人聲如沸闪檬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粗悯。三九已至虚循,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背横缔。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工铺遂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人茎刚。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓襟锐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親膛锭。 傳聞我的和親對象是個殘疾皇子粮坞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354