window與windowManager
- window在日常開發(fā)中如懸浮窗,它的實(shí)現(xiàn)是phoneWindow
- window的創(chuàng)建通過WindowManager,WindowManager是外界訪問window的入口。
windowManager獲?。?/p>
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
window通過setWindowManager()方法關(guān)聯(lián)WindowManager
- window的具體實(shí)現(xiàn)位于WindowManagerService,WindowManager和WindowManagerService的交互是一個IPC過程
- Android所有的視圖都是通過Window來呈現(xiàn)的,不管是Activity,Dialog,還是Toast,它們的視圖實(shí)際上都是附加在Window上,因此,Window實(shí)際是View的實(shí)際管理者,
- Activity設(shè)置視圖的方法setContentView()底層也是通過Window來實(shí)現(xiàn)的
- Window有3種類型
- 應(yīng)用類window 對應(yīng)一個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是分層的,每個Window都有對應(yīng)的z-ordered,層級大的會覆蓋在層級小的上面,和html的z-index概念完全一致*
層級分別是上面的范圍,對應(yīng)著WindowManager.LayoutParams.type參數(shù),想要覆蓋在最上面,設(shè)置層級范圍最大即可,即系統(tǒng)層級,一般選用:
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR
同時聲明權(quán)限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- WindowManage僅提供了三個方法,供開發(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來處理,WindowManagerGlobal以工廠的形式向外提供實(shí)例-----典型的橋接模式
Activity Window創(chuàng)建流程
- Activity的Window的創(chuàng)建在attach()方法里完成的,在attach()方法里,系統(tǒng)會創(chuàng)建Activity所屬的Window對象并為其設(shè)置回調(diào)接口,Window對象的創(chuàng)建是通過PolicyManager的makeNewWindow方法實(shí)現(xiàn)的
由于Activity實(shí)現(xiàn)了Window的CallBack接口,因此Window接受到外界的回調(diào)就會回調(diào)到Activity中的方法,如onAttachToWindow(),onDetachFromWindow(),dispatchTouchEvent()
PolicyManager 一個策略類,PolicyManager中實(shí)現(xiàn)的幾個工廠方法全部在策略接口IPolicy中聲明了,其中makeNewWindow()方法中完成new PhoneWindow()。
(25版本中源碼Activity的attch方法中直接new PhoneWindow(),并沒有工廠模式)
- Activity視圖怎么依附到Window上的
通過setContentView()來實(shí)現(xiàn)依附,setContent具體實(shí)現(xiàn):
- 如果沒有DecorView,就創(chuàng)建它
- 將View添加到DecorView的mContentParent中
- 回調(diào)Activity的onContentChanged()方法通知Activity的視圖已經(jīng)發(fā)生了改變
在ActivityThread中的handleResumeActivity方法中,首先會調(diào)用onResume方法,然后再調(diào)用Activity的makeVivible()方法,正是在makeVisible()方法中,DecorView真正完成了顯示和添加這兩個過程到這里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方法來完成,高版本直接new PhoneWindow();參考源碼
- 創(chuàng)建步驟
- 創(chuàng)建Window
- 初始化DecorView,并將Dialog視圖添加到DecorView中
setContentView()
-
將DecorView添加到Window中并顯示
在Dialog的show()方法中,通過WindowManager將DecorView添加到Window中,mWindowManager.addView(mDecor, l); mShowing = true;
- 普通Dialog的Context必須使用Activity的Context,如果使用ApplicationContext,會報錯。普通Dialog需要依附一個Window
Toast的Window創(chuàng)建過程
- Toast 內(nèi)部有兩類IPC通信過程
- Toast訪問NotificationManagerService
- NotificationManagerService回調(diào)TN里的接口
- TN是一個Binder類,用于NMS跨進(jìn)程調(diào)用TN的方法,如hide(),show()
所有TN里的hide,show()等方法都運(yùn)行在Binder線程池中,所以需要Handle切換到當(dāng)前線程中去。
注意是切換到當(dāng)前線程中。在沒有Looper的線程中,Toast無法正確運(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對象,并將其添加到mToastQueue隊(duì)列中,mToastQueue其實(shí)是一個ArrayList,對于非系統(tǒng)應(yīng)用來說,該list長度最多為50個ToastRecord,這么做是為了防止DOS(拒絕服務(wù)攻擊),也就是如果大量循環(huán)彈toast那么其他應(yīng)用就無法彈了。
- Toast 原理也就是Binder通信,Toast調(diào)用Show方法,內(nèi)部會將該toast與TN傳遞給運(yùn)行于系統(tǒng)進(jìn)程的NMS中,由系統(tǒng)進(jìn)程控制TN的show方法,系統(tǒng)需要統(tǒng)一管理toast,NMS會將該toast放入一個ArrayList執(zhí)行隊(duì)列中,while循環(huán)輪到該條toast時,取出該toast對于的TN對象,再通過Binder通信,執(zhí)行遠(yuǎn)程TN對象的show方法,show方法會通過Handle讓show過程脫離Binder線程池,運(yùn)行于Looper線程中,show中實(shí)際通過WindowManage的addView方法,將View添加到window上。這樣就完成了一次Toast顯示過程,hide過程一樣。