Activity與Window相關(guān)概念
- Activity負責生命周期
- Window控制視圖顯示相關(guān)、事件處理相關(guān)
- AMS統(tǒng)一調(diào)度所有應(yīng)用程序的Activity
- WMS控制所有Window的顯示與隱藏
Window
“Window”表明它是和窗口相關(guān)的,“窗口”是一個抽象的概念,
1、從用戶的角度來講,它是一個“界面”;
2、從SurfaceFlinger的角度來看,它是一個Layer,承載著和界面有關(guān)的數(shù)據(jù)和屬性;
3、從WMS角度來看,它是一個WIndowState,用于管理和界面有關(guān)的狀態(tài)。
4,從WindowManager角度來講,它是一個容器存儲虛擬Window的數(shù)據(jù),被WindowManager管理
- 表示一個窗口的概念,是所有View的直接管理者,任何視圖都通過Window呈現(xiàn)(點擊事件由Window->DecorView->View; Activity的setContentView底層通過Window完成)
- Window是一個抽象類,具體實現(xiàn)有PhoneWindow。例如activity::attach與Dialog的構(gòu)造方法中
- Window是抽象出來的概念,需要WindowManager來管理(添加、刪除、更新)。
- WindowManager是外界訪問Window的入口
- Window具體實現(xiàn)位于WindowManagerService中
- 定義窗口樣式和行為的抽象基類,用于作為頂層的view加到WindowManager中,其實現(xiàn)類是PhoneWindow。
- 每個Window都需要指定一個Type(應(yīng)用窗口、子窗口、系統(tǒng)窗口)。Activity對應(yīng)的窗口是應(yīng)用窗口;PopupWindow,ContextMenu,OptionMenu是常用的子窗口;像Toast和系統(tǒng)警告提示框(如ANR)就是系窗口,還有很多應(yīng)用的懸浮框也屬于系統(tǒng)窗口類型。
WindowManager
管理窗口(添加、刪除、更新),WindowManager只是面向開發(fā)者調(diào)用的一個中間類,其并不持有WindowManagerService的句柄,而是創(chuàng)建出ViewRootImpl與WindowManagerService通信,每個Windowd都有一個ViewRootImpl。
//WindowManager的獲取 ContextImpl::getSystemService -> SystemServiceRegistry::getSystemService
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//WindowManager操作View
public void addView(View view, ViewGroup.LayoutParams params);//顯示View 調(diào)用:Activity::makeVisible(),Dialog:show()
public void updateViewLayout(View view, ViewGroup.LayoutParams params);//更新View
public void removeView(View view);//移除View
-
view:要顯示的View,常用的有Activity::PhoneWindow::DecorView、Dialog::PhoneWindow::DecorView、Toast::makeText,自己顯示W(wǎng)indow的View等等。
-
ViewGroup.LayoutParams:本質(zhì)WindowManager.LayoutParams,添加自己的Window就要配置這個參數(shù)
- 1、配置type,應(yīng)用窗口:層級范圍是1~99、子窗口:層級范圍是1000~1999、系統(tǒng)窗口:層級范圍是2000~2999
- 2、配置 flags,常用的有
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//不獲取焦點
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL//窗口之外事件可以傳遞到底層window
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED//顯示鎖屏Window -
addView顯示View 調(diào)用:Activity::makeVisible(),Dialog:show(),自己顯示W(wǎng)indow的View。
WindowManagerImpl(Window里找)::addView-> WindowManagerGlobal::addView(window添加view的時候創(chuàng)建ViewRootImpl) -> ViewRootImpl::setView
//Window
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);//設(shè)置WindowManager
}
//WindowManagerImpl
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);//驗證params是否是WindowManager.LayoutParams
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
//WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
//ViewRootImpl
final IWindowSession mWindowSession;//
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...//
requestLayout();
...//Bindler機制向Session通信內(nèi)部調(diào)用了WindowManagerService::addWindow
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
....
}
//執(zhí)行scheduleTraversals 開始走View流程
//Session 全局搜索IWindowSession可以找到Session類
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override//調(diào)用WindowManagerService::addWindow
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
}
//WindowManagerService
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
int res = mPolicy.checkAddPermission(attrs, appOp);//WindowManagerPolicy(PhoneWindowManager) window顯示策略
...
mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
...
res = mPolicy.prepareAddWindowLw(win, attrs);//調(diào)用prepareAddWindowLw()執(zhí)行Window的添加
mWindowMap.put(client.asBinder(), win);//以Binder為Key將WindowState對象保存在mWindowMap中
win.mToken.addWindow(win);//將WindowState保存在窗口的Token中
}
-
removeView 調(diào)用:ActivityThread::handleDestroyActivity,Dialog:dismiss()等等。
WindowManagerImpl::removeViewImmediate-> WindowManagerGlobal::removeView()-> ViewRootImpl::die
//ActivityThread
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
...
wm.removeViewImmediate(v);
}
//WindowManagerGlobal
public void removeView(View view, boolean immediate) {
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
//ViewRootImpl
boolean die(boolean immediate) {
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
void doDie() {
checkThread();
synchronized (this) {
mRemoved = true;
if (mAdded) {
dispatchDetachedFromWindow();//執(zhí)行view.onDetachedFromWindow、 mSurface.release()、 mWindowSession.remove(mWindow);等等釋放操作
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);//結(jié)束繪制window
}
} catch (RemoteException e) {
}
}
mSurface.release();
}
}
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}
-
updateViewLayout 同理
//WindowManagerGlobal
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
//ViewRootImpl
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
...
scheduleTraversals();
}
}
- WindowManagerGlobal 管理DecorView,mParams,mDyingViews,ViewRootImpl 等集合。
- ViewRootImpl 非常重要 負責view measure layout draw event 統(tǒng)一管理。
- 可以看出Window只是虛構(gòu)出來的架子,而ViewRootImpl 和 WMS才是控制Window關(guān)鍵先生。
WindowManagerService
簡稱Wms,WindowManagerService管理窗口的創(chuàng)建、更新和刪除,顯示順序等,是WindowManager這個管理接品的真正的實現(xiàn)類。它運行在System_server進程,作為服務(wù)端,客戶端(應(yīng)用程序)通過IPC調(diào)用和它進行交互。
Token
這里提到的Token主是指窗口令牌(Window Token),是一種特殊的Binder令牌,Wms用它唯一標識系統(tǒng)中的一個窗口。
創(chuàng)建Window視圖配置參數(shù)type
應(yīng)用窗口:層級范圍是1~99
子窗口:層級范圍是1000~1999
系統(tǒng)窗口:層級范圍是2000~2999
各級別type值在WindowManager中的定義分別為:
應(yīng)用窗口(1~99)
//第一個應(yīng)用窗口
public static final int FIRST_APPLICATION_WINDOW = 1;
//所有程序窗口的base窗口,其他應(yīng)用程序窗口都顯示在它上面
public static final int TYPE_BASE_APPLICATION = 1;
//所有Activity的窗口,只能配合Activity在當前APP使用
public static final int TYPE_APPLICATION = 2;
//目標應(yīng)用窗口未啟動之前的那個窗口
public static final int TYPE_APPLICATION_STARTING = 3;
//最后一個應(yīng)用窗口
public static final int LAST_APPLICATION_WINDOW = 99;
- 子窗口(1000~1999)
//第一個子窗口
public static final int FIRST_SUB_WINDOW = 1000;
// 面板窗口,顯示于宿主窗口的上層,只能配合Activity在當前APP使用
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
// 媒體窗口(例如視頻),顯示于宿主窗口下層
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1;
// 應(yīng)用程序窗口的子面板,只能配合Activity在當前APP使用(PopupWindow默認就是這個Type)
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
//對話框窗口,只能配合Activity在當前APP使用
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
//
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4;
//最后一個子窗口
public static final int LAST_SUB_WINDOW = 1999;
- 系統(tǒng)窗口(2000~2999)
//系統(tǒng)窗口,非應(yīng)用程序創(chuàng)建
public static final int FIRST_SYSTEM_WINDOW = 2000;
//狀態(tài)欄,只能有一個狀態(tài)欄,位于屏幕頂端,其他窗口都位于它下方
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
//搜索欄,只能有一個搜索欄,位于屏幕上方
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
//
//電話窗口,它用于電話交互(特別是呼入),置于所有應(yīng)用程序之上,狀態(tài)欄之下,屬于懸浮窗(并且給一個Activity的話按下HOME鍵會出現(xiàn)看不到桌面上的圖標異常情況)
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
//
//系統(tǒng)警告提示窗口,出現(xiàn)在應(yīng)用程序窗口之上,屬于懸浮窗, 但是會被禁止
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
//
//信息窗口,用于顯示Toast, 不屬于懸浮窗, 但有懸浮窗的功能, 缺點是在Android2.3上無法接收點擊事件
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
//
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
//鎖屏窗口
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
//系統(tǒng)頂層窗口,顯示在其他一切內(nèi)容之上,此窗口不能獲得輸入焦點,否則影響鎖屏
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;
//電話優(yōu)先,當鎖屏時顯示,此窗口不能獲得輸入焦點,否則影響鎖屏
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
//系統(tǒng)對話框窗口
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
//鎖屏時顯示的對話框
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
//系統(tǒng)內(nèi)部錯誤提示,顯示在任何窗口之上
public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;
//內(nèi)部輸入法窗口,顯示于普通UI之上,應(yīng)用程序可重新布局以免被此窗口覆蓋
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
//內(nèi)部輸入法對話框,顯示于當前輸入法窗口之上
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
//墻紙窗口
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
//狀態(tài)欄的滑動面板
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
//安全系統(tǒng)覆蓋窗口,這些窗戶必須不帶輸入焦點,否則會干擾鍵盤
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
//最后一個系統(tǒng)窗口
public static final int LAST_SYSTEM_WINDOW = 2999;
2.窗口flags顯示屬性在WindowManager中也有定義:
//窗口特征標記
public int flags;
//當該window對用戶可見的時候,允許鎖屏
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
//窗口后面的所有內(nèi)容都變暗
public static final int FLAG_DIM_BEHIND = 0x00000002;
//Flag:窗口后面的所有內(nèi)容都變模糊
public static final int FLAG_BLUR_BEHIND = 0x00000004;
//窗口不能獲得焦點
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
//窗口不接受觸摸屏事件
public static final int FLAG_NOT_TOUCHABLE = 0x00000010;
//即使在該window在可獲得焦點情況下,允許該窗口之外的點擊事件傳遞到當前窗口后面的的窗口去
public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
//當手機處于睡眠狀態(tài)時,如果屏幕被按下,那么該window將第一個收到觸摸事件
public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
//當該window對用戶可見時,屏幕出于常亮狀態(tài)
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
//:讓window占滿整個手機屏幕,不留任何邊界
public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;
//允許窗口超出整個手機屏幕
public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200;
//window全屏顯示
public static final int FLAG_FULLSCREEN = 0x00000400;
//恢復(fù)window非全屏顯示
public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
//開啟窗口抖動
public static final int FLAG_DITHER = 0x00001000;
//安全內(nèi)容窗口,該窗口顯示時不允許截屏
public static final int FLAG_SECURE = 0x00002000;
//鎖屏時顯示該窗口
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
//系統(tǒng)的墻紙顯示在該窗口之后
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
//當window被顯示的時候,系統(tǒng)將把它當做一個用戶活動事件,以點亮手機屏幕
public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
//該窗口顯示,消失鍵盤
public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
//當該window在可以接受觸摸屏情況下,讓因在該window之外,而發(fā)送到后面的window的觸摸屏可以支持split touch
public static final int FLAG_SPLIT_TOUCH = 0x00800000;
//對該window進行硬件加速,該flag必須在Activity或Dialog的Content View之前進行設(shè)置
public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000;
//讓window占滿整個手機屏幕,不留任何邊界
public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000;
//透明狀態(tài)欄
public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
//透明導(dǎo)航欄
public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;