window與windowManager
- window在日常開發(fā)中如懸浮窗猴抹,它的實(shí)現(xiàn)是phoneWindow
- window的創(chuàng)建通過(guò)WindowManager曼振,WindowManager是外界訪問(wèn)window的入口互艾。
windowManager獲壬缏丁:
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
window通過(guò)setWindowManager()方法關(guān)聯(lián)WindowManager
- window的具體實(shí)現(xiàn)位于WindowManagerService,WindowManager和WindowManagerService的交互是一個(gè)IPC過(guò)程
- Android所有的視圖都是通過(guò)Window來(lái)呈現(xiàn)的胁后,不管是Activity店读,Dialog,還是Toast,它們的視圖實(shí)際上都是附加在Window上,因此攀芯,Window實(shí)際是View的實(shí)際管理者屯断,
- Activity設(shè)置視圖的方法setContentView()底層也是通過(guò)Window來(lái)實(shí)現(xiàn)的
- Window有3種類型
- 應(yīng)用類window 對(duì)應(yīng)一個(gè)Activity (1~99)
- 子window 不能單獨(dú)存在,需要依附在特定的父window之中侣诺,比如Dialog (1000~1999)
- 系統(tǒng)window 需要聲明權(quán)限才能創(chuàng)建的window殖演,比如toast,系統(tǒng)狀態(tài)欄 (2000~2999)
- Window是分層的年鸳,每個(gè)Window都有對(duì)應(yīng)的z-ordered,層級(jí)大的會(huì)覆蓋在層級(jí)小的上面趴久,和html的z-index概念完全一致*
層級(jí)分別是上面的范圍,對(duì)應(yīng)著WindowManager.LayoutParams.type參數(shù)搔确,想要覆蓋在最上面彼棍,設(shè)置層級(jí)范圍最大即可灭忠,即系統(tǒng)層級(jí),一般選用:
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR
同時(shí)聲明權(quán)限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- WindowManage僅提供了三個(gè)方法座硕,供開發(fā)者繼承使用:
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實(shí)現(xiàn)類是WindowManagerImpl
WindowManagerImpl并沒有直接實(shí)現(xiàn)Window的三大操作弛作,而是全部交給WindowManagerGlobal來(lái)處理,WindowManagerGlobal以工廠的形式向外提供實(shí)例-----典型的橋接模式
Activity Window創(chuàng)建流程
- Activity的Window的創(chuàng)建在attach()方法里完成的华匾,在attach()方法里映琳,系統(tǒng)會(huì)創(chuàng)建Activity所屬的Window對(duì)象并為其設(shè)置回調(diào)接口,Window對(duì)象的創(chuàng)建是通過(guò)PolicyManager的makeNewWindow方法實(shí)現(xiàn)的
由于Activity實(shí)現(xiàn)了Window的CallBack接口蜘拉,因此Window接受到外界的回調(diào)就會(huì)回調(diào)到Activity中的方法萨西,如onAttachToWindow(),onDetachFromWindow(),dispatchTouchEvent()
PolicyManager 一個(gè)策略類,PolicyManager中實(shí)現(xiàn)的幾個(gè)工廠方法全部在策略接口IPolicy中聲明了诸尽,其中makeNewWindow()方法中完成new PhoneWindow()原杂。
(25版本中源碼Activity的attch方法中直接new PhoneWindow(),并沒有工廠模式)
- Activity視圖怎么依附到Window上的
通過(guò)setContentView()來(lái)實(shí)現(xiàn)依附,setContent具體實(shí)現(xiàn):
- 如果沒有DecorView您机,就創(chuàng)建它
- 將View添加到DecorView的mContentParent中
- 回調(diào)Activity的onContentChanged()方法通知Activity的視圖已經(jīng)發(fā)生了改變
在ActivityThread中的handleResumeActivity方法中穿肄,首先會(huì)調(diào)用onResume方法,然后再調(diào)用Activity的makeVivible()方法,正是在makeVisible()方法中际看,DecorView真正完成了顯示和添加這兩個(gè)過(guò)程到這里Activity才被用戶看到咸产。
//Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
Dialog Window創(chuàng)建流程
- Dialog使用的是PhoneWindow,同樣低版本使用了PlicyManager的makeNewWindow方法來(lái)完成,高版本直接new PhoneWindow();參考源碼
- 創(chuàng)建步驟
- 創(chuàng)建Window
- 初始化DecorView仲闽,并將Dialog視圖添加到DecorView中
setContentView()
-
將DecorView添加到Window中并顯示
在Dialog的show()方法中脑溢,通過(guò)WindowManager將DecorView添加到Window中,mWindowManager.addView(mDecor, l); mShowing = true;
- 普通Dialog的Context必須使用Activity的Context赖欣,如果使用ApplicationContext,會(huì)報(bào)錯(cuò)屑彻。普通Dialog需要依附一個(gè)Window
Toast的Window創(chuàng)建過(guò)程
- Toast 內(nèi)部有兩類IPC通信過(guò)程
- Toast訪問(wèn)NotificationManagerService
- NotificationManagerService回調(diào)TN里的接口
- TN是一個(gè)Binder類,用于NMS跨進(jìn)程調(diào)用TN的方法顶吮,如hide(),show()
所有TN里的hide,show()等方法都運(yùn)行在Binder線程池中社牲,所以需要Handle切換到當(dāng)前線程中去。
注意是切換到當(dāng)前線程中悴了。在沒有Looper的線程中搏恤,Toast無(wú)法正確運(yùn)行。
- Toast的show()方法
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
enqueueToast首先將Toast封裝成ToastRecord對(duì)象湃交,并將其添加到mToastQueue隊(duì)列中熟空,mToastQueue其實(shí)是一個(gè)ArrayList,對(duì)于非系統(tǒng)應(yīng)用來(lái)說(shuō),該list長(zhǎng)度最多為50個(gè)ToastRecord,這么做是為了防止DOS(拒絕服務(wù)攻擊)搞莺,也就是如果大量循環(huán)彈toast那么其他應(yīng)用就無(wú)法彈了息罗。
- Toast 原理也就是Binder通信,Toast調(diào)用Show方法才沧,內(nèi)部會(huì)將該toast與TN傳遞給運(yùn)行于系統(tǒng)進(jìn)程的NMS中阱当,由系統(tǒng)進(jìn)程控制TN的show方法俏扩,系統(tǒng)需要統(tǒng)一管理toast,NMS會(huì)將該toast放入一個(gè)ArrayList執(zhí)行隊(duì)列中弊添,while循環(huán)輪到該條toast時(shí)录淡,取出該toast對(duì)于的TN對(duì)象,再通過(guò)Binder通信油坝,執(zhí)行遠(yuǎn)程TN對(duì)象的show方法嫉戚,show方法會(huì)通過(guò)Handle讓show過(guò)程脫離Binder線程池,運(yùn)行于Looper線程中澈圈,show中實(shí)際通過(guò)WindowManage的addView方法彬檀,將View添加到window上。這樣就完成了一次Toast顯示過(guò)程瞬女,hide過(guò)程一樣窍帝。