1. 程式人生 > >Android WMS分析(一) WindowManager

Android WMS分析(一) WindowManager

1.WindowManager ,WMS,Window三者關係

WindowManager是一個介面類,繼承自介面ViewManager,負責管理Window,他的實現類是WindowManagerImpl。如果我們相對Window進行新增,更新,刪除,就需要通過WindowManager,其會將具體的工作交給WindowManagerService(WMS)處理

而Window是一個抽象類,具體實現類是PhoneWindow。Window包含了view並對view進行管理。Window是一個抽象概念,用來描述一個視窗,並不是真實存在的,Window的實體其實也是View。

2.一些重要的類介紹

2.1 ViewManager.java

WindowManager是一個介面類,繼承自介面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);
}

從名稱可以看出三個方法分別對應了View的新增,更新,刪除

WindowManager在繼承ViewManager的同時,也加入了很多功能。其中有兩個方法是根據Window的特性加入的

public Display getDefaultDisplay();
public void removeViewImmediate(View view);

getDefaultDisplay()方法能夠知道這個WindowManager例項將Window新增到哪個螢幕上,換句話說就是得到WindowManager所管理的螢幕(Display )。

removeViewImmediate()方法則貴的再這個方法返回前要立即執行View.onDetachedFromWindow(),來完成傳入的View相關的銷燬工作

2.2 Window和PhoneWindow

Window是一個抽象類,具體實現類是PhoneWindow

下面我們看下PhoneWindow是何時建立的

在Activity啟動過程中會呼叫ActivityThread的preformLaunchActivity()方法,preformLaunchActivity()中會呼叫Activity.java的attach()方法,PhoneWindow就是在attach()中new出來的

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ......
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        ......
    }

無論是Dialog還是Activity都是通過Window物件的setWindowManager方法將WindowManagerWindow關聯。該函式是在Window中,看看實現:

    /**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
        setWindowManager(wm, appToken, appName, false);
    }

    /**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    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);
    }

WindowManagerImpl.java-->createLocalWindowManager()

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

可以看到就是建立了WindowManager的例項WindowManagerImpl,同時將Window作為引數傳了進來,這樣WindowManagerImpl就持有了Window的引用,可以對Window進行操作,比如在Window中新增View

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

可以看出WindowManagerImpl雖然是WindowManager的實現類,但是沒有實現什麼功能,而是將功能實現委託給了WindowManagerGlobal,這就是典型的橋接模式

3.Window的屬性

Window的型別有很多種,必然要應用程式視窗,輸入法視窗,PopupWindow,Toast,Dialog......。總的來說Window分為三大型別,分別是Application Window(應用程式視窗),Sub Window(子視窗),System Window(系統視窗),每個大型別中又包含了很多種型別,它們都定義在WindowManager的靜態內部類LayoutParams中,接下來分別對著三大型別進行分析。

3.1應用程式視窗

Activity就是典型的應用程式視窗,應用程式視窗包含以下型別:

        /**
         * Start of window types that represent normal application windows.
         *開始表示正常應用程式視窗的視窗型別。
         */
        public static final int FIRST_APPLICATION_WINDOW = 1;//應用程式視窗的初始值

        /**
         * Window type: an application window that serves as the "base" window
         * of the overall application; all other application windows will
         * appear on top of it.
         * In multiuser systems shows only on the owning user's window.
         *一個應用程式視窗,用作整個應用程式的“基礎”視窗; 所有其他應用程式視窗將顯示在它上面。
         */
        public static final int TYPE_BASE_APPLICATION   = 1;

        /**
         * Window type: a normal application window.  The {@link #token} must be
         * an Activity token identifying who the window belongs to.
         * In multiuser systems shows only on the owning user's window.
         *普通的應用程式視窗型別
         */
        public static final int TYPE_APPLICATION        = 2;

        /**
         * Window type: special application window that is displayed while the
         * application is starting.  Not for use by applications themselves;
         * this is used by the system to display something until the
         * application can show its own windows.
         * In multiuser systems shows on all users' windows.
         *用於再應用程式視窗啟動前顯示的視窗
         */
        public static final int TYPE_APPLICATION_STARTING = 3;

        /**
         * Window type: a variation on TYPE_APPLICATION that ensures the window
         * manager will wait for this window to be drawn before the app is shown.
         * In multiuser systems shows only on the owning user's window.
         */
        public static final int TYPE_DRAWN_APPLICATION = 4;
        /**
         * End of types of application windows.
         */
        public static final int LAST_APPLICATION_WINDOW = 99;//應用程式視窗的結束值

3.2 子視窗

子視窗,不能獨立存在,需依附其他窗口才可以,PopupWindow就屬於子視窗。子視窗包含以下型別:

/**
         * Start of types of sub-windows.  The {@link #token} of these windows
         * must be set to the window they are attached to.  These types of
         * windows are kept next to their attached window in Z-order, and their
         * coordinate space is relative to their attached window.
         */
        public static final int FIRST_SUB_WINDOW = 1000;

        /**
         * Window type: a panel on top of an application window.  These windows
         * appear on top of their attached window.
         */
        public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

        /**
         * Window type: window for showing media (such as video).  These windows
         * are displayed behind their attached window.
         */
        public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

        /**
         * Window type: a sub-panel on top of an application window.  These
         * windows are displayed on top their attached window and any
         * {@link #TYPE_APPLICATION_PANEL} panels.
         */
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

        /** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout
         * of the window happens as that of a top-level window, <em>not</em>
         * as a child of its container.
         */
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

        /**
         * Window type: window for showing overlays on top of media windows.
         * These windows are displayed between TYPE_APPLICATION_MEDIA and the
         * application window.  They should be translucent to be useful.  This
         * is a big ugly hack so:
         * @hide
         */
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;

        /**
         * Window type: a above sub-panel on top of an application window and it's
         * sub-panel windows. These windows are displayed on top of their attached window
         * and any {@link #TYPE_APPLICATION_SUB_PANEL} panels.
         * @hide
         */
        public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;

        /**
         * End of types of sub-windows.
         */
        public static final int LAST_SUB_WINDOW = 1999;

可以看出子視窗的Tpye值範圍是1000~1999

3.3系統視窗

系統視窗包括Toast,輸入法視窗,系統音量條視窗。。。


public static final int FIRST_SYSTEM_WINDOW     = 2000;
public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
......
public static final int LAST_SYSTEM_WINDOW      = 2999;

可以看出系統視窗的Tpye值範圍是2000~2999

4. Window的標誌

Window的標誌也就是Flag,用於控制Window的顯示,同樣被定義在WindowManager的內部類LayoutParams

int FLAGS_CHANGED 用於表示flags發生了變化,關於此的詳細內容請看後文。
int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON Window flag: as long as this window is visible to the user, allow the lock screen to activate while the screen is on.
當該window對使用者可見的時候,允許鎖屏。
int FLAG_ALT_FOCUSABLE_IM Window flag: invert the state of FLAG_NOT_FOCUSABLE with respect to how this window interacts with the current method.
int FLAG_BLUR_BEHIND Window flag: blur everything behind this window.
讓該window後所有東西都模糊(blur)
int FLAG_DIM_BEHIND Window flag: everything behind this window will be dimmed.
讓該window後所有的東西都成暗淡(dim)
int FLAG_DISMISS_KEYGUARD Window flag: when set the window will cause the keyguard to be dismissed, 
only if it is not a secure lock keyguard.
int FLAG_DITHER Window flag: turn on dithering when compositing this window to the screen.
開啟抖動(dithering)
int FLAG_FORCE_NOT_FULLSCREEN Window flag: Override {@link #FLAG_FULLSCREEN and force the screen decorations (such as status bar) to be shown.
恢復window非全屏顯示
int FLAG_FULLSCREEN Window flag: Hide all screen decorations (e.g.
讓window進行全屏顯示
int FLAG_HARDWARE_ACCELERATED

Indicates whether this window should be hardware accelerated.

對該window進行硬體加速.

該flag必須在設定你的Activity或Dialog的Content View之前進行設定,

而且如果你在mainfest檔案中用Android:hardwareAccelerated開啟了該屬性的話,那麼你在程式中就不能再改變它。mainfest檔案中android:hardwareAccelerated屬性預設是開啟的("true")。

int FLAG_IGNORE_CHEEK_PRESSES Window flag: intended for windows that will often be used when the user is holding the screen against their face, it will aggressively filter the event stream to prevent unintended presses in this situation that may not be desired for a particular window, when such an event stream is detected, the application will receive a CANCEL motion event to indicate this so applications can handle this accordingly by taking no action on the event until the finger is released.
int FLAG_KEEP_SCREEN_ON Window flag: as long as this window is visible to the user, keep the device's screen turned on and bright.
當該window對使用者可見時,讓裝置螢幕處於高亮(bright)狀態。
int FLAG_LAYOUT_INSET_DECOR Window flag: a special option only for use in combination with FLAG_LAYOUT_IN_SCREEN.
int FLAG_LAYOUT_IN_SCREEN Window flag: place the window within the entire screen, ignoring decorations around the border (a.k.a.
讓window佔滿整個手機螢幕,不留任何邊界(border)
int FLAG_LAYOUT_NO_LIMITS Window flag: allow window to extend outside of the screen.
window大小不再不受手機螢幕大小限制,即window可能超出螢幕之外,這時部分內容在螢幕之外。
int FLAG_NOT_FOCUSABLE Window flag: this window won't ever get key input focus, so the user can not send key or other button events to it.
讓window不能獲得焦點,這樣使用者快就不能向該window傳送按鍵事件及按鈕事件
int FLAG_NOT_TOUCHABLE Window flag: this window can never receive touch events.
讓該window不接受觸控式螢幕事件
int FLAG_NOT_TOUCH_MODAL Window flag: Even when this window is focusable (its {@link #FLAG_NOT_FOCUSABLE is not set), 
allow any pointer events outside of the window to be sent to the windows behind it.
即使在該window在可獲得焦點情況下,仍然把該window之外的任何event傳送到該window之後的其他window.
int FLAG_SCALED Window flag: a special mode where the layout parameters are used to perform scaling of the surface when it is composited to the screen.
int FLAG_SECURE Window flag: don't allow screen shots while this window is displayed.
當該window在進行顯示的時候,不允許截圖。
int FLAG_SHOW_WALLPAPER Window flag: ask that the system wallpaper be shown behind your window.
在該window後顯示系統的牆紙(wallpaper)
int FLAG_SHOW_WHEN_LOCKED Window flag: special flag to let windows be shown when the screen is locked.
當鎖屏的時候,顯示該window.
int FLAG_SPLIT_TOUCH Window flag: when set the window will accept for touch events outside of its bounds to be sent to other windows that also support split touch. When this flag is not set, the first pointer that goes down determines the window to which all subsequent touches go until all pointers go up. When this flag is set, each pointer (not necessarily the first) that goes down determines the window to which all subsequent touches of that pointer will go until that pointer goes up thereby enabling touches with multiple pointers to be split across multiple windows
當該window在可以接受觸控式螢幕情況下,讓因在該window之外,而傳送到後面的window的觸控式螢幕可以支援split touch.
int FLAG_TOUCHABLE_WHEN_WAKING Window flag: When set, if the device is asleep when the touch screen is pressed, you will receive this first touch event.
當手機處於睡眠狀態時,如果螢幕被按下,那麼該window將第一個收到到事件
int FLAG_TURN_SCREEN_ON Window flag: when set as a window is being added or made visible, once the window has been shown then the system will poke the power manager's user activity (as if the user had woken up the device) to turn the screen on.
當然window被顯示的時候,系統將把它當做一個使用者活動事件,以點亮手機螢幕。
int FLAG_WATCH_OUTSIDE_TOUCH Window flag: if you have set FLAG_NOT_TOUCH_MODAL, you can set this flag to receive a single special MotionEvent with the action MotionEvent.ACTION_OUTSIDE 
for touches that occur outside of your window.
如果你設定了該flag,那麼在你FLAG_NOT_TOUNCH_MODAL的情況下,即使觸控式螢幕事件傳送在該window之外,其事件被髮送到了後面的window,那麼該window仍然將以MotionEvent.ACTION_OUTSIDE形式收到該觸控式螢幕事件

設定Window的Flag有三種方法,

第一種addFlags:

Window mWindow = getWindow()
mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    public void addFlags(int flags) {
        setFlags(flags, flags);
    }

第二種setFlags:

Window mWindow = getWindow()
mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
    public void setFlags(int flags, int mask) {
        final WindowManager.LayoutParams attrs = getAttributes();
        attrs.flags = (attrs.flags&~mask) | (flags&mask);
        mForcedWindowFlags |= mask;
        dispatchWindowAttributesChanged(attrs);
    }

第三種則是給LayoutParams設定Flag,並通過WindowManager的addView方法進行新增

LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams();
textParams.flag = LayoutParams.FLAG_FULLSCREEN;
WindowManager mWindowManager  = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
TextView mTextView  = new TextView(this);
mWindowManager.addView(mTextView ,textParams );