前言
本文已經(jīng)收錄到我的Github個人博客,歡迎大佬們光臨寒舍:
學(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)部機制焰坪、自定義等等方面會有更加深入的體會趣倾。
注意:
StatusBar
也包含在DecorView
之內(nèi)
二.核心知識點歸納
2.1 Window
關(guān)系解析
看到下面這張大圖,是不是感覺有點亂亂的琳彩,別急誊酌,別急,心急吃不了熱豆腐露乏,筆者將向您娓娓道來
2.1.1 Window
&PhoneWindow
筆者之前在進(jìn)階之路 | 奇妙的View之旅中碧浊,提及
setContentView
的時候簡單說到了Window
和PhoneWindow
,相信看過的讀者已經(jīng)對此有一個簡單的印象瘟仿。
Window
是一個抽象類箱锐,它定義了頂級窗體樣式和行為。其唯一的實現(xiàn)類是PhoneWindow
劳较。
2.1.2 Window
&View
筆者之前在進(jìn)階之路 | 奇妙的View之旅中驹止,提及
View工作流程
的時候簡單說到了ViewRootImpl
,相信看過的讀者已經(jīng)對此有一個簡單的印象观蜗。
每個Window
都對應(yīng)一個View
和一個ViewRootImpl
臊恋,Window
和View
通過ViewRootImpl
來建立聯(lián)系。Window
并不可見墓捻,它實際以View
的形式存在抖仅,它是View
的直接管理者。
2.1.3 Window
&WindowManagerService
想了解
IPC
的讀者砖第,可以看下筆者寫的一篇博客: 進(jìn)階之路 | 奇妙的 IPC 之旅
Window
的具體實現(xiàn)位于WindowManagerService
中撤卢。WindowManager
和WindowManagerService
的交互是一個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
,OptionMenu
是Dialog
的子類梯找,它們修改了自身的窗口類型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"/>
,Android
6.0以下直接聲明權(quán)限即可锈锤,Android6.0
以上還需要用戶打開軟件設(shè)置頁手動打開驯鳖,才能授權(quán)闲询。
Window
是分層的,見下表- 層級大的會覆蓋在層級小的
Window
上面浅辙。- 對應(yīng)
WindowManager.LayoutParams
的type
參數(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)部機制
-
WindowManager
對Window
主要有三大操作:添加鸽捻、更新和刪除。這三個方法主要是定義在
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)系:
因此厚满,通過
WindowManagerGlobal
的addView()
、updateViewLayout()
碧磅、removeView()
實現(xiàn)WindowManager
對Window
的添加痰滋、刪除和修改。
2.2.1 Window
的添加
2.2.2 Window
的刪除
2.2.3 Window
的更新
不難發(fā)現(xiàn)续崖,以上驗證了之前的總結(jié):
Window
的三大操作最終都會通過一個IPC過程移交給WindowManagerService
敲街。Window
和View
通過ViewRootImpl
來聯(lián)系,ViewRootImpl
可控制View
的測量严望、布局和重繪多艇。
限于篇幅,筆者這里暫未貼上源碼像吻,如果想了解的話峻黍,推薦一篇文章:我眼中的Window創(chuàng)建/添加/刪除/更新過程
2.3 Window
的創(chuàng)建過程
由于
View
必須依附Window
才能呈現(xiàn)出來,因此有View
的地方必有Window
拨匆。在Android
中可以提供View
的地方有Activity
姆涩、Dialog
和Toast
,PopupWindow
,菜單
惭每,下面分別來看Activity
骨饿、Dialog
和Toast
三種Window
的大致創(chuàng)建過程
2.3.1 Activity
的Window
創(chuàng)建過程
想詳細(xì)了解
Activity
的啟動流程的,推薦一篇筆者寫的文章:進(jìn)階之路 | 奇妙的四大組件之旅
想了解
Activity
的Window
創(chuàng)建過程的源碼的讀者台腥,筆者推薦一篇文章: Activity的Window創(chuàng)建過程分析
2.3.2 Dialog
的Window
創(chuàng)建過程
-
Dialog.show()
:完成DecorView
的顯示 -
WindowManager.remoteViewImmediate()
方法:當(dāng)Dialog
被dismiss
時移除DecorView
2.3.3 Toast
的Window
創(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)建過程的源碼的讀者,筆者推薦一篇文章: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
等等坑傅。 - 可能有人會說:“
Window
是View
的管理者∨缯” - 追問:我們知道唁毒,
WindowManager
是Window
的管理者,那為什么不直接用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)用,Android
讓Activity
幫我們管理好文留,我們只需簡單的去重寫幾個回調(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)的動力
本文參考鏈接:
- 《Android 開發(fā)藝術(shù)探索》
- 橋接模式
- 我眼中的Window創(chuàng)建/添加/刪除/更新過程
- Activity的Window創(chuàng)建過程分析
- Android對話框Dialog是己,PopupWindow,Toast的實現(xiàn)機制
- 學(xué)習(xí)筆記|AS入門(番外) 技能篇
- 要點提煉|開發(fā)藝術(shù)之Window
- Android Window 機制探索
- 理清Activity任柜、View及Window之間關(guān)系
- android 6.0之后 android.permission.SYSTEM_ALERT_WINDOW使用方法變動
- 一個app有多少個window卒废?
- 淺析 Android 的窗口
- DecorView 包含Status Bar么?