SurfaceFlinger 前面說的,就是一個surface的合成。SurfaceFlinger就是一個默默的記錄着,它不會對surface的內容有什么改動。
WMS(WindowsManagerService)就是對surface的管理,或者說是一個大管家。它負責協調各方面資源。
ViewRoot就是一個個演員,負責表演(產生surface)。
從IO系統角度而言,WMS至少要干這幾件事。
全局窗口管理
全局事件派發
鍵盤
觸摸屏
1.WMS綜述
1)WMS將以同AMS等一樣的形式,系統server的一部分。
由SystemServer負責啟動
知道系統關閉才能停止
發生異常的時候,能夠自我恢復
2)SurfaceFlinger 和WMS將有很多交集。
3)有顯示需求的圖層。可以想見,界面顯示是分不同層級的。
4)inputManagerService 當有按鍵或者觸摸事件時,WMS時最好的管理員。
5)AMS 同WMS 也有交互。
6)Bind交互
從WMS窗口的實現來講,主要包含如下子功能
窗口的添加和刪除
啟動窗口
窗口動畫
窗口大小
窗口層級
事件派發
1.1WMS的啟動
services\java\com\android\server\SystemServer.java
private void startOtherServices() { inputManager = new InputManagerService(context); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); traceBeginAndSlog("StartWindowManagerService"); wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); }
1.2 WMS提供的服務
public class IWindowManagerImpl implements IWindowManager
提供了很多功能,包含屏幕獲取,獲取窗口大小,啟動窗口等
1.3 WMS工作方式
WMS很復雜,以工作方式作為切入點是比較合適的。
WMS,AMS,Activity之間的關系
WMS 可以和AMS相互調用,Activity有Window的對象。
public ViewRootImpl(Context context, Display display) { mContext = context; mWindowSession = WindowManagerGlobal.getWindowSession();
public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { Log.e(TAG, "Failed to open window session", e); } } return sWindowSession; } }
可以看到,是windowmanager提供的一個session
當啟動一個activity的時候,AMS會把記錄放到activityRecord。同時WMS會對activity進行記錄,就用WindowState
2.窗口屬性
2.1窗口的層級和類型
窗口的類型很多,不過,可以統一划分為3類,Application Window,System Window,Sub Window.
它們全部定義在WindowManager.java
2.1.1 普通窗口
2.1.2 Sub Window
這一類主要就是Dialog 之類的。
2.1.3 系統窗口
系統窗口非常多,主要由系統狀態欄,來電,輸入法等。
具體的取值:
Application Window:1-99
SubWindow:100-1999
SystemWindow:2000-2999
當某個進程向WMS申請一個Window的時候,需要告訴系統窗口的類型。如果有3個app在運行中,則前台有3個窗口,這個時候,需要能調整它們的優先級。
對於Window的顯示,層級越高,顯示越前面。這個顯示的動作,由SurfaceFlinger來處理。
if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw( attachedWindow.mAttrs.type) * WindowManagerService.TYPE_LAYER_MULTIPLIER + WindowManagerService.TYPE_LAYER_OFFSET; mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type); mAttachedWindow = attachedWindow; ......
所有窗口的mBaseLayer可以分幾步獲得:
@Step1:windowTypeToLayerLw
這個根據不同的窗口類型做了簡單的映射。

@Override public int windowTypeToLayerLw(int type) { if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { return 2; } switch (type) { case TYPE_PRIVATE_PRESENTATION: return 2; case TYPE_WALLPAPER: // wallpaper is at the bottom, though the window manager may move it. return 2; case TYPE_PHONE: return 3; case TYPE_SEARCH_BAR: case TYPE_VOICE_INTERACTION_STARTING: return 4; case TYPE_VOICE_INTERACTION: // voice interaction layer is almost immediately above apps. return 5; case TYPE_INPUT_CONSUMER: return 6; case TYPE_SYSTEM_DIALOG: return 7; case TYPE_TOAST: // toasts and the plugged-in battery thing return 8; case TYPE_PRIORITY_PHONE: // SIM errors and unlock. Not sure if this really should be in a high layer. return 9; case TYPE_DREAM: // used for Dreams (screensavers with TYPE_DREAM windows) return 10; case TYPE_SYSTEM_ALERT: // like the ANR / app crashed dialogs return 11; case TYPE_INPUT_METHOD: // on-screen keyboards and other such input method user interfaces go here. return 12; case TYPE_INPUT_METHOD_DIALOG: // on-screen keyboards and other such input method user interfaces go here. return 13; case TYPE_KEYGUARD_SCRIM: // the safety window that shows behind keyguard while keyguard is starting return 14; case TYPE_STATUS_BAR_SUB_PANEL: return 15; case TYPE_STATUS_BAR: return 16; case TYPE_STATUS_BAR_PANEL: return 17; case TYPE_KEYGUARD_DIALOG: return 18; case TYPE_VOLUME_OVERLAY: // the on-screen volume indicator and controller shown when the user // changes the device volume return 19; case TYPE_SYSTEM_OVERLAY: // the on-screen volume indicator and controller shown when the user // changes the device volume return 20; case TYPE_NAVIGATION_BAR: // the navigation bar, if available, shows atop most things return 21; case TYPE_NAVIGATION_BAR_PANEL: // some panels (e.g. search) need to show on top of the navigation bar return 22; case TYPE_SYSTEM_ERROR: // system-level error dialogs return 23; case TYPE_MAGNIFICATION_OVERLAY: // used to highlight the magnified portion of a display return 24; case TYPE_DISPLAY_OVERLAY: // used to simulate secondary display devices return 25; case TYPE_DRAG: // the drag layer: input for drag-and-drop is associated with this window, // which sits above all other focusable windows return 26; case TYPE_ACCESSIBILITY_OVERLAY: // overlay put by accessibility services to intercept user interaction return 27; case TYPE_SECURE_SYSTEM_OVERLAY: return 28; case TYPE_BOOT_PROGRESS: return 29; case TYPE_POINTER: // the (mouse) pointer layer return 30; } Log.e(TAG, "Unknown window type: " + type); return 2; }
對於sub window而言,窗口類型取決於父窗口類型。
@Step2. 上一步獲得的值*TYPE_LAYER_MULTIPLIER(10000)+TYPE_LAYER_OFFSET(1000)
同一類型的窗口可能由很多。1000.是為了移動一組window而設計的。
@Step3.subWindowTypeToLayerLw 計算子窗口的layer。偏移量在1,或者-2都有可能。所以子窗口現在在父窗口的上面。或者下面,都可能。
2.2窗口屬性
windowmanagerprolicy。android顯示的同一的規則。手機有StatusBar,而平板有CombinedBar。類似,但是功能不一樣。
/** * WindowManagerPolicy implementation for the Android phone UI. This * introduces a new method suffix, Lp, for an internal lock of the * PhoneWindowManager. This is used to protect some internal state, and * can be acquired with either the Lw and Li lock held, so has the restrictions * of both of those when held. */ public class PhoneWindowManager implements WindowManagerPolicy
PhoneWindowManager 和call沒有關系,它表述andorid phone的UI顯示規則。
2.3 layoutParams
1)Type
窗口類型,不再說明。
2)Flags
最典型的就是,保持屏幕常亮。這個可以使用FLAG_KEEP_SCREEN_ON。
在activity下使用這個方法就可以:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON:只要此窗口可見,即使屏幕點亮,也允許鎖屏。 從現實來講,有這個需求嗎? 測驗后,實際效果,會半暗屏,但不會上鎖。
FLAG_DIM_BEHIND:在窗口后面的東西,都將變暗。 dialog之類的比較常用。一個非常有用的flag,尤其是需要做蒙層的時候。

public class DimDialog extends Dialog { private static final float DIMDIALOG_TRANTANT = 0.3f; private static final float DIMDIALOG_BG_TRANTANT = 0.8f; public DimDialog(Context context) { super(context); setAttributes(); } private void setAttributes() { WindowManager.LayoutParams lp=getWindow().getAttributes(); lp.alpha=DIMDIALOG_TRANTANT; lp.dimAmount = DIMDIALOG_BG_TRANTANT; getWindow().setAttributes(lp); getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); } public DimDialog(Context context, int themeResId) { super(context, themeResId); setAttributes(); } protected DimDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { super(context, cancelable, cancelListener); setAttributes(); } }
FLAG_BLUR_BEHIND:高斯模糊,目前已經廢棄,對性能的影響巨大,不建議使用。
FLAG_NOT_FOCUSABLE:窗口不處理事件,將會傳遞到后面的其他窗口。同時FLAG_NOT_TOUCH_MODAL也會被設置。
FLAG_NOT_TOUCHABLE:touch 事件傳遞到后面的窗口使用。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
FLAG_KEEP_SCREEN_ON:最常用,最經典的模式,屏幕常量
FLAG_FULLSCREEN:全屏,沒有狀態欄。
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
動態調整全屏狀態:

private void full(boolean enable) { if (enable) { WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; getWindow().setAttributes(lp); getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } else { WindowManager.LayoutParams attr = getWindow().getAttributes(); attr.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().setAttributes(attr); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); } }
FLAG_FORCE_NOT_FULLSCREEN:同FLAG_FULLSCREEN相反
FLAG_SECURE:窗口無法被截屏。不安全的應用也無法顯示等。
FLAG_SCALED:按用戶的要求調整窗口
FLAG_IGNORE_CHEEK_PRESSES: 當屏幕有可能貼着臉時,這一選項可防止面頰對屏幕造成誤操作。
FLAG_LAYOUT_INSET_DECOR:只能和FLAG_LAYOUT_IN_SCREEN一起使用,充分考慮各種情況
FLAG_SHOW_WHEN_LOCKED:在鎖屏的時候,可以顯示該頁面,也是非常重要的flag。關於鎖屏的問題可以參考 鎖屏上顯示Activity 這篇博客,此處不再敘述。
FLAG_SHOW_WALLPAPER:當前activity為透明或者半透明的時候,讓壁紙作為背景。
WindowManager.LayoutParams lp=getWindow().getAttributes(); lp.alpha = 0.5f; getWindow().setAttributes(lp);//設置透明度 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);//設置壁紙
FLAG_TURN_SCREEN_ON:把屏幕點亮
FLAG_DISMISS_KEYGUARD:解鎖。是指普通鎖屏,但是安全鎖(圖案或者密碼鎖屏界面)是無效的。 在電話界面,可以直接解鎖,而不需進入鎖屏界面。
FLAG_HARDWARE_ACCELERATED:硬件加速,具體取決於硬件條件。
3)systemUiVisibilty
這個flag,定義在View中,
這里先明確,這些flag起作用,首先這個view必須是可見的。而window的flag是全局的。
View.SYSTEM_UI_FLAG_VISIBLE:顯示狀態欄 View.INVISIBLE 對應屬性,隱藏狀態欄。
View.SYSTEM_UI_FLAG_FULLSCREEN 和上節講到的FLAG_FULLSCREEN具有相同的效果,具體的細微差距,請參考http://www.360doc.com/content/15/0204/18/20385871_446270224.shtml 這篇文章。此處不做詳細討論。
根據經驗,View可以是臨時的,而FLAG_FULLSCREEN 可以是長期的。
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 可以是navigationbar隱藏。
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN activity全屏顯示,但是statusbar不會隱藏,會把activity上面的部分,覆蓋。
View.SYSTEM_UI_FLAG_IMMERSIVE ,在5.1上測試的結果同SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 類似,activity全屏,但statusbar仍然存在。半透明的狀態,但是狀態欄點擊會有反映。
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 在statusbar隱藏后,過幾秒會自動出現。
這里還要注意getWindow().getDecorView().setSystemUiVisibility();同setContentView(layout.activity_wall_layout);的順序問題。