1. 程式人生 > >WindowManagerService原始碼學習

WindowManagerService原始碼學習

WindowManagerService 原始碼

Android 的framework 層主要是由 WindowManagerService 與 ActivityManagerService 以及 View 所構成,這三個模組穿插互動在 framework中。

WMS 和其他很多服務一樣, 都是由 SystemServer 啟動。

在SystemServer 中,有如下程式碼:

wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY
_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm);

addService中 key 是 window。 那麼在其他程序就可以通過 ServiceManager 以key 為 window 來獲取到 WMS。

下面是WMS 中的 main 方法。 通過 Handler 的 runWithScissors 方法執行一個特殊的同步 Task. 並構建了 WMS 的例項。

public static
WindowManagerService main(final Context context, final InputManagerService im, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; //執行同步 task DisplayThread.getHandler().runWithScissors(new
Runnable() { @Override public void run() { //構建例項 holder[0] = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore); } }, 0); return holder[0]; }

Handler 的 runWithScissors 方法中 第一行註釋說明這是一個同步 task。如下:

  • Runs the specified task synchronously.

WMS 的構造方法中主要是對一些視窗管理將要用到的成員變數 進行初始化:

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
        mContext = context;
        mHaveInputMethods = haveInputMethods;
        mAllowBootMessages = showBootMsgs;
        mOnlyCore = onlyCore;
        mLimitedAlphaCompositing = context.getResources().getBoolean(
                com.android.internal.R.bool.config_sf_limitedAlpha);
        mHasPermanentDpad = context.getResources().getBoolean(
                com.android.internal.R.bool.config_hasPermanentDpad);
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mDisplaySettings = new DisplaySettings(context);

        // ... 省略幾行程式碼
        //獲取顯示服務
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        Display[] displays = mDisplayManager.getDisplays();
        //為每一個 Display 分配一個 Content
        for (Display display : displays) {
            createDisplayContentLocked(display);
        }
         // ... 省略幾行程式碼

         //構建App 事務
        mAppTransition = new AppTransition(context, mH);
         //獲取 IActivityManager 物件
        mActivityManager = ActivityManagerNative.getDefault();
        // ... 省略幾行程式碼
        //構造 Window 動畫物件
        mAnimator = new WindowAnimator(this);

         // 初始化視窗策略
        initPolicy();

         //開啟繪製事務
        SurfaceControl.openTransaction();
        // ... 省略幾行程式碼
    }

WMS 主要功能有兩個方面, 一是 對視窗的管理; 二是 對事件的管理和分發。

介面方法定義在 IWindowManager.aidl。 編譯後會生成 IWindowManager.java介面檔案。 裡面定義了 WMS 絕大部分的功能和方法。

WMS 定義了許多各種不同的視窗。

     //已經完成啟動的應用
    final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();

     //漸變視窗
    final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<FakeWindowImpl>();

     //尺寸正在改變的視窗
    final ArrayList<WindowState> mResizingWindows = new ArrayList<WindowState>();

     //動畫正在結束的視窗
    final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>();

     //即將釋放的Surface視窗
    final ArrayList<WindowState> mDestroySurface = new ArrayList<WindowState>();

     // 失去焦點的Window
    ArrayList<WindowState> mLosingFocus = new ArrayList<WindowState>();

    //為了釋放記憶體需要強制關閉的Window
    ArrayList<WindowState> mForceRemoves;

    //正在開啟的應用
    final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<AppWindowToken>();    

     //正在關閉的應用  
    final ArraySet<AppWindowToken> mClosingApps = new ArraySet<AppWindowToken>();

     //得到焦點的應用      
    AppWindowToken mFocusedApp = null;

    ... //省略程式碼 還有很多。

不同的視窗或者同一個視窗在不同的階段可能會位於不同的表中。

Android 中主要由兩種視窗型別:

1.應用視窗。

如 Activity所處的視窗,對話方塊視窗、彈出的視窗等等。 與應用相關的 Window類的 PhoneWindow。 核心是DecorView, 通過 addView 將一個DecorView 新增到 WindowManager.

2.系統視窗。

如 螢幕頂部的狀態列 、 底部的導航欄、桌面的視窗等。 系統視窗沒有針對的封裝類, 只需要通過 WindwoManager的 addView方法 新增到WindowManager 即可。

比如PhoneStatusBar 中。 createAndAddWindows() -> StatusBarWindowManager.java -> add()中:

mWindowManager.addView(mStatusBarView, mLp);

客戶端新增一個視窗

WindowManager.addView() -> WindowManagerImpl.addView() -> WindowMangerGlobal.addView() -> ViewRootImpl.setView() -> IWindowSession.addToDisplay() -> Session.addToDisplay() -> WMS.addWindow().

Sesson中:

@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets,
            InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outInputChannel);
    }

ok 。我們來看 WMS 中 addWindow 的實現:

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, InputChannel outInputChannel) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }
        ...// 省略一堆程式碼...
    }

這裡的 mPolicy 實際上是 PhoneWindowManager. Policy 中的實現。

 public WindowManagerPolicy makeNewWindowManager() {
     return new PhoneWindowManager();
}

PhoneWindowManager 的 checkAddPermission 中會先判斷視窗的 type 是否是系統級別。 如果不是 則返回 WindowManagerGlobal.ADD_OKAY. 如果是,就會進行相關許可權等的校驗 SYSTEM_ALERT_WINDOWINTERNAL_SYSTEM_WINDOW. 如果沒有許可權就會 返回 ADD_PERMISSION_DENIED. 校驗完畢許可權 會校驗視窗的有效性。


            boolean addToken = false;
            WindowToken token = mTokenMap.get(attrs.token);
            if (token == null) {
                    //如果視窗是子視窗
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                //如果是輸入法視窗
                if (type == TYPE_INPUT_METHOD) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
               ...// 省略一堆 if else.

檢查視窗的有效性完畢,接下來就是為該視窗建立一個 WindowState物件。維護視窗的狀態以及更加適當的機制來調整視窗狀態。

        synchronized(mWindowMap) {

              // ...省略程式碼

              //構建WindowState物件
            win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            //如果客戶端已經被銷燬        
            if (win.mDeathRecipient == null) {
                return WindowManagerGlobal.ADD_APP_EXITING;
            }
              //根據視窗型別調整屬性
            mPolicy.adjustWindowParamsLw(win.mAttrs);

            //設定該視窗是否只對自己的 uid 可見
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
              //根據視窗型別判定許可權
            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }
              //如果輸出 Channel 的通道為空
            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                //建立通道    
                String name = win.makeInputChannelName();
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);
                   //向 InputManager 中註冊該通道,以便當前視窗可以接受到事件。
                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }

            //...省略一些程式碼

mPolicy 實質上就是 PhoneWindowManager。 對用它的 adjustWindowParamsLw 方法。裡面判斷如果是 TYPE_SYSTEM_OVERLAY 和 TYPE_SECURE_SYSTEM_OVERLAY 型別 的視窗,不要獲取焦點即可。

addWindow 最後部分的邏輯

    ... 省略大量程式碼
   res = WindowManagerGlobal.ADD_OKAY;
              //重置當前執行緒 IPC 的 ID
            origId = Binder.clearCallingIdentity();
              是否需要token
            if (addToken) {
                mTokenMap.put(attrs.token, token);
            }
            //將視窗新增至Session
            win.attach();
            //將視窗新增至 WMS
            mWindowMap.put(client.asBinder(), win);

              //省略幾行程式碼...

              //順序排列視窗
            if (type == TYPE_INPUT_METHOD) {
                    //如果是輸入法視窗
                win.mGivenInsetsPending = true;
                mInputMethodWindow = win;
                addInputMethodWindowToListLocked(win);
                imMayMove = false;
            } else if (type == TYPE_INPUT_METHOD_DIALOG) {
                  //如果是輸入法對話方塊視窗
                mInputMethodDialogs.add(win);
                addWindowToListInOrderLocked(win, true);
                moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
                imMayMove = false;
            } else {
                  //如果是其他視窗
                addWindowToListInOrderLocked(win, true);
                if (type == TYPE_WALLPAPER) {
                    mLastWallpaperTimeoutTime = 0;
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if (mWallpaperTarget != null
                        && mWallpaperTarget.mLayer >= win.mBaseLayer) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
            }

            win.mWinAnimator.mEnterAnimationPending = true;
              //如果displayContent 是預設的顯示
            if (displayContent.isDefaultDisplay) {
                   //獲取系統視窗區域的inserts
                mPolicy.getContentInsetHintLw(attrs, outContentInsets);
            } else {
                    //否則將 outContentInsets 置為空
                outContentInsets.setEmpty();
            }

            if (mInTouchMode) {
                    //標識使用者直接觸控的視窗
                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
            }
            if (win.mAppToken == null || !win.mAppToken.clientHidden) {
                    //標識應用視窗
                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
            }

            mInputMonitor.setUpdateInputWindowsNeededLw();

            boolean focusChanged = false;
            //如果當前視窗可以接收按鍵事件
            if (win.canReceiveKeys()) {
                   //那麼更新焦點將視窗資訊存入 InputDispatcher
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }

            if (imMayMove) {
                moveInputMethodWindowsIfNeededLocked(false);
            }

              //分配最終層級
            assignLayersLocked(displayContent.getWindowList());

            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);

            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                reportNewConfig = true;
            }
        }

        if (reportNewConfig) {
            sendNewConfiguration();
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }

這部分邏輯中,最重要的一步就是通過 WindowState的 attach方法 建立一個關聯的 SurfaceSession的物件 用以與 SurfaceFlinger通訊。 而 SurfaceSession實際對應的是 native層中的 SurfaceComposerClient物件。 SurfaceComposerClient 主要作用就是在 應用程序與 SurfaceFlinger 服務之間建立連線。(這塊不是很明白。很多知識都不理解)。

mSurfaceSession = new SurfaceSession();