深入理解 WindowManagerService

在上篇文章中 初步理解 Window 體系,我們初步分析了 Window 的體系,這篇文章我們分析一下 WindowManagerService(以下簡稱 WMS)。WMS 錯綜負責,與 ActivityManagerService、InputManagerService、SurfaceFlinger 關系也很緊密,如果想分析的清楚徹底,恐怕是一兩篇文章難以做到的。本篇文章初步分析 WMS 的創建,以及應用進程中的 WindowManager 與 WMS 通信。

一. 理解 WindowManagerService 相關知識

1.1 WindowManagerService 的誕生

1.1.1 在 SystemServer 中的創建

WMS 是在 SystemServer 進程中啟動的,SystemServer 進程是 Android 系統啟動的時候初始化的,我們首先來看一下 SystemServer 的入口函數 main()

public final class SystemServer {

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }
}

從上述代碼可見,是創建了一個 SystemServer 對象并調用了 run() 方法


    private void run() {

        ......
        mSystemServiceManager = new SystemServiceManager(mSystemContext);  // 代碼 1

        // Start services.
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();                              // 代碼 2
            startCoreServices();                                   // 代碼 3
            startOtherServices();                                  // 代碼 4
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            traceEnd();
        }
    }
  • 代碼1處,創建了一個 SystemServiceManager 對象,用于創建各種系統服務并管理他們的生命周期
  • 代碼2處,調用 startBootstrapServices() 啟動 ActivityManagerService、PackageManagerService 等服務進程
  • 代碼3處,調用 startCoreServices() 啟動BatteryService、WebViewUpdateService 等服務進程 startOtherServices() 啟動
  • 代碼4處,調用 startOtherServices() 啟動 WindowManagerService、InputManagerService 等服務進程

startOtherServices() 方法很長,我們分析下其中和 WMS 相關的部分

    private void startOtherServices() {
        final Context context = mSystemContext;
        WindowManagerService wm = null;
        InputManagerService inputManager = null;
        
        ......
        try {
            // 代碼 1
            traceBeginAndSlog("StartInputManagerService");
            inputManager = new InputManagerService(context);
            traceEnd();

            // 代碼 2
            traceBeginAndSlog("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
            traceEnd();

            // 代碼 3
            traceBeginAndSlog("SetWindowManagerService");
            mActivityManagerService.setWindowManager(wm);
            traceEnd();

            // 代碼 4
            traceBeginAndSlog("StartInputManager");
            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
            inputManager.start();
            traceEnd();
            ......
        } catch (RuntimeException e) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting core service", e);
        }
        ......
        // 代碼 5
        traceBeginAndSlog("MakeDisplayReady");
        try {
            wm.displayReady();
        } catch (Throwable e) {
            reportWtf("making display ready", e);
        }
        traceEnd();
        ......
        // 代碼 6
        traceBeginAndSlog("MakeWindowManagerServiceReady");
        try {
            wm.systemReady();
        } catch (Throwable e) {
            reportWtf("making Window Manager Service ready", e);
        }
        traceEnd();

        if (safeMode) {
            mActivityManagerService.showSafeModeOverlay();
        }
        // 代碼 7
        // Update the configuration for this context by hand, because we're going
        // to start using it before the config change done in wm.systemReady() will
        // propagate to it.
        final Configuration config = wm.computeNewConfiguration(DEFAULT_DISPLAY);
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager w = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        w.getDefaultDisplay().getMetrics(metrics);
        context.getResources().updateConfiguration(config, metrics);
        ......
    }
  • 代碼1處,創建了 InputManagerService 對象,InputManagerService 主要用于接收系統的輸入事件,包括按鍵、觸摸等
  • 代碼2處,調用 WindowManager.main() 方法創建 WindowManagerService 對象,并將 WindowManagerService 和 InputManagerService 對象添加到 ServiceManager 中
  • 代碼3處,為 ActivityManagerService 對象設置 WindowManagerService 對象
  • 代碼4處,為 InputManagerService 設置 WindowManagerService 對象的 InputMonitor 對象,并啟動 InputManagerService 對象
  • 代碼5處,調用 WMS 的 displayReady() 方法初始化顯示信息
  • 代碼6處,調用 WMS 的 systemReady() 方法通知 WMS 系統的初始化工作完成
  • 代碼7處,為 Context 中的 WindowManagerImpl 實例對象設置 DisplayMetrics 對象,并更新當前 Context 的 Resources 中的 Configuration 和 DisplayMetircs 屬性
1.1.2 WMS 的構造函數

在上面一段代碼中,最重要的莫過于調用 WMS 的 main() 方法創建一個 WindowManagerService 對象了,我們來分析下這個方法

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

    private static WindowManagerService sInstance;
    static WindowManagerService getInstance() {
        return sInstance;
    }

    public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }

}
  • 我們看到在 main() 方法中,在 DisplayThread 線程中通過 WMS 的構造方法創建一個 WMS 實例對象
  • DisplayThread 線程是一個系統前臺線程,用于執行一些延時要非常小的關于顯示的操作,一般只會在 WindowManager、DisplayManager 和 InputManager 中使用,代碼也比較簡單,如下所示:
    /**
     * Shared singleton foreground thread for the system.  This is a thread for
     * operations that affect what's on the display, which needs to have a minimum
     * of latency.  This thread should pretty much only be used by the WindowManager,
     * DisplayManager, and InputManager to perform quick operations in real time.
     */
    public final class DisplayThread extends ServiceThread {
        private static DisplayThread sInstance;
        private static Handler sHandler;
    
        private DisplayThread() {
            // DisplayThread runs important stuff, but these are not as important as things running in
            // AnimationThread. Thus, set the priority to one lower.
            super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/);
        }
    
        private static void ensureThreadLocked() {
            if (sInstance == null) {
                sInstance = new DisplayThread();
                sInstance.start();
                sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                sHandler = new Handler(sInstance.getLooper());
            }
        }
    
        public static DisplayThread get() {
            synchronized (DisplayThread.class) {
                ensureThreadLocked();
                return sInstance;
            }
        }
    
        public static Handler getHandler() {
            synchronized (DisplayThread.class) {
                ensureThreadLocked();
                return sHandler;
            }
        }
    }
    

我們接著上面的 main() 方法分析,上面代碼調用了 WMS 的構造方法創建了 WMS 實例對象,我們來看一下 WMS 中的一些重要的成員屬性和構造方法,如下所示:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

    final WindowManagerPolicy mPolicy;
    final ArraySet<Session> mSessions = new ArraySet<>();
    final WindowHashMap mWindowMap = new WindowHashMap();
    final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();

    final H mH = new H();

    final InputManagerService mInputManager;

    final WindowAnimator mAnimator;

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {

        ......
        // 代碼 1
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        // 代碼 2 
        mPolicy = policy;
        if(mInputManager != null) {
            final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
            mPointerEventDispatcher = inputChannel != null
                    ? new PointerEventDispatcher(inputChannel) : null;
        } else {
            mPointerEventDispatcher = null;
        }

        ......
        // 代碼 3
        mAnimator = new WindowAnimator(this);

        ......
        // 代碼 4
        initPolicy();

        // 代碼 5
        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
        ......
    }
}
  • 代碼1 處,保存 SystemServer 中傳入的 InputManagerService 實例對象,輸入事件最終要分發給具有焦點的窗口,而 WMS 是窗口的管理者。mInputManager 用于管理每個窗口的輸入事件通道,并向通道上派發事件
  • 代碼2 處,mPolicy 對象是 WMS 中非常重要的一個對象,是 WindowManagerPolicy 類型的,WindowManagerPolicy(簡稱 WMP) 是一個接口,具體的實現類是 PhoneWindowManagermPolicy 對象可以說是 WMS 的首席顧問,WMS 的許多操作都是需要 WMP 規定的,比如:多個窗口的上下順序,監聽屏幕旋轉的狀態,預處理一些系統按鍵事件(例如HOME、BACK鍵等的默認行為就是在這里實現的)
  • 代碼3 處,創建一個 WindowAnimator 對象,用于管理所有窗口的動畫
  • 代碼4 處,初始化 mPolicy 對象
  • 代碼5 處,將 WMS 實例對象本身添加到 Watchdog 中,WMS 類實現了 Watchdog.Monitor 接口。Watchdog 用于監控系統的一些關鍵服務

1.2 WMS 中的幾個重要概念

除開上面構造方法中提到的一些成員屬性之外,還有一些非常重要的概念需要理解

1.2.1 Session

在上篇文章 初步理解 Window 體系 中,我們提到 ViewRootImpl 和 WMS 之間的通信就是通過 Session 對象完成的。
Session 類繼承自 IWindowSession.Stub,每一個應用進程都有一個唯一的 Session 對象與 WMS 通信,如下圖所示

Session.png

圖片來源:Window與WMS通信過程

在 WMS 中的 mSessions 成員屬性,是 ArraySet 類型,其中保存著 Session 型的對象。

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
        /**
         * All currently active sessions with clients.
         */
        final ArraySet<Session> mSessions = new ArraySet<>();

        ......

        @Override
        public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
            if (client == null) throw new IllegalArgumentException("null client");
            if (inputContext == null) throw new IllegalArgumentException("null inputContext");
            Session session = new Session(this, callback, client, inputContext);
            return session;
    }
}
public class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
    final WindowManagerService mService;
    private int mNumWindow = 0;                  // 代碼 1
    ......

    // 代碼 2
    public Session(WindowManagerService service, IWindowSessionCallback callback,
            IInputMethodClient client, IInputContext inputContext) {
        mService = service;
        ......
    }

    void windowAddedLocked(String packageName) {
        mPackageName = packageName;
        mRelayoutTag = "relayoutWindow: " + mPackageName;
        if (mSurfaceSession == null) {
            if (WindowManagerService.localLOGV) Slog.v(
                TAG_WM, "First window added to " + this + ", creating SurfaceSession");
            mSurfaceSession = new SurfaceSession();
            if (SHOW_TRANSACTIONS) Slog.i(
                    TAG_WM, "  NEW SURFACE SESSION " + mSurfaceSession);
            // 代碼 3
            mService.mSessions.add(this);
            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                mService.dispatchNewAnimatorScaleLocked(this);
            }
        }
        mNumWindow++;
    }

    void windowRemovedLocked() {
        mNumWindow--;
        killSessionLocked();
    }

    private void killSessionLocked() {
        if (mNumWindow > 0 || !mClientDead) {
            return;
        }
        // 代碼 4
        mService.mSessions.remove(this);
        ......
    }

}
  • 代碼 1 處,mNumWindow 變量記錄著此 Session 中共有多少個 Window
  • 代碼 2 處的 Session 的構造方法中,mService 保存著 WMS 的實例對象
  • 代碼 3 處,將此 Session 對象添加進 WMS 的 mSessions 隊列中
  • 代碼 4 處,將此 Session 對象從 WMS 的 mSessions 隊列中移除
1.2.2 WindowState

WindowState 是 WMS 中一個重要的概念,在 WMS 中的一個 WindowState 對象就對應著一個應用進程中的 Window 對象。
我們在上篇文章 初步理解 Window 體系 最后,在調用WindowManagerGlobal.addView 方法時,經過一系列的方法調用,最后走到了 WindowManagerService.addWindow 方法中

addWindow.png

在 WindowManagerService.addWindow 方法中,會創建一個與 Window 對象對應的 WindowState 對象并調用 WindowState.attach 方法,然后將該 WindowState 對象添加到 WMS 的 mWindowMap Map 中

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

    final WindowHashMap mWindowMap = new WindowHashMap();

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
            ......

            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            ......
            win.attach();
            mWindowMap.put(client.asBinder(), win);
            ......
            win.mToken.addWindow(win);
    }
}
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
    final WindowManagerService mService;
    final WindowManagerPolicy mPolicy;
    final Context mContext;
    final Session mSession;
    final IWindow mClient;


    WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
           WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
           int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
        mService = service;
        mSession = s;
        mClient = c;
        mAppOp = appOp;
        mToken = token;
        mAppToken = mToken.asAppWindowToken();

        ......
    }

    void attach() {
        if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
        mSession.windowAddedLocked(mAttrs.packageName);
    }
    
    ......
}

在 WindowState 中保存了 WMS 對象、WMP 對象、Session 對象和 IWindow 對象,IWindow 對象就是與此 WindowState 對象相對應的在應用進程中的 Window 對象。

final WindowHashMap mWindowMap = new WindowHashMap(); 是一個 HashMap 的子類,key 是 IBinder,value 是 WindowState,用于保存 WMS 中所有的 WindowState 對象,

/**
 * Subclass of HashMap such that we can instruct the compiler to boost our thread priority when
 * locking this class. See makefile.
 */
class WindowHashMap extends HashMap<IBinder, WindowState> {
}
 mWindowMap.put(client.asBinder(), win);

IWindow client 對象,其實是 ViewRootImpl 中的 final W mWindow 成員,如下所示:

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    final W mWindow;

    ......

    static class W extends IWindow.Stub {
        ......
    }
    ......
}
1.2.3 WindowToken

簡單的理解,WindowToken 有兩個作用:

  1. 在 WMS 中,一個 WindowToken 就代表著一個應用組件,應用組件包括:Activity、InputMethod 等。在 WMS 中,會將屬于同一 WindowToken 的做統一處理,比如在對窗口進行 ZOrder 排序時,會將屬于統一 WindowToken 的排在一起。
  2. WindowToken 也具有令牌的作用。應用組件在創建 Window 時都需要提供一個有效的 WindowToken 以表明自己的身份,并且窗口的類型必須與所持有的 WindowToken 類型保持一致。如果是系統類型的窗口,可以不用提供 WindowToken,WMS 會自動為該系統窗口隱式的創建 WindowToken,但是要求應用必須具有創建該系統類型窗口的權限

概念上有了初步的理解,我們來看下 WindowToken 的代碼,如下所示,在 WindowToken 類中,最重要的其實是其中的成員屬性

/**
 * Container of a set of related windows in the window manager. Often this is an AppWindowToken,
 * which is the handle for an Activity that it uses to display windows. For nested windows, there is
 * a WindowToken created for the parent window to manage its children.
 */
class WindowToken extends WindowContainer<WindowState> {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;

    // The window manager!
    protected final WindowManagerService mService;

    // The actual token.
    final IBinder token;

    // The type of window this token is for, as per WindowManager.LayoutParams.
    final int windowType;    
    ......

    // The display this token is on.
    protected DisplayContent mDisplayContent;
    ......

    WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
            DisplayContent dc, boolean ownerCanManageAppTokens) {
        mService = service;
        token = _token;
        windowType = type;
        mPersistOnEmpty = persistOnEmpty;
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;
        onDisplayChanged(dc);
    }

    void onDisplayChanged(DisplayContent dc) {
        dc.reParentWindowToken(this);
        mDisplayContent = dc;

        // TODO(b/36740756): One day this should perhaps be hooked
        // up with goodToGo, so we don't move a window
        // to another display before the window behind
        // it is ready.
        SurfaceControl.openTransaction();
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowState win = mChildren.get(i);
            win.mWinAnimator.updateLayerStackInTransaction();
        }
        SurfaceControl.closeTransaction();

        super.onDisplayChanged(dc);
    }

    ......

}

其實,對于 WMS 來講,只要是一個 IBinder 對象都可以作為 Token,比如在之前分析添加 Window 時,調用 WindowManagerService.addWindow 方法時,傳入的 Token 對象就是一個 IWindow.Stub 的對象。我們來看一下 WMS 中的 addWindowToken 方法,如下所示:

    @Override
    public void addWindowToken(IBinder binder, int type, int displayId) {
        if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }

        synchronized(mWindowMap) {
            final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
            // 代碼 1
            WindowToken token = dc.getWindowToken(binder);
            // 代碼 2
            if (token != null) {
                Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
                        + " for already created window token: " + token
                        + " displayId=" + displayId);
                return;
            }
            // 代碼 3
            if (type == TYPE_WALLPAPER) {
                new WallpaperWindowToken(this, binder, true, dc,
                        true /* ownerCanManageAppTokens */);
            } else {
                new WindowToken(this, binder, type, true, dc, true /* ownerCanManageAppTokens */);
            }
        }
    }
  • 代碼 1 處,從 DisplayContent 中取一個 WindowToken 對象。從這兒可以看出每一個 WindowToken 又具體是屬于每個 DisplayContent 對象的,DisplayContent 對象可以理解為一塊屏幕的對應,這個概念在之后詳細介紹。
    class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
    
        WindowManagerService mService;
        // mTokenMap 一個 HashMap 對象,用于映射 IBinder 和 WindowToken 對象
        // Mapping from a token IBinder to a WindowToken object on this display.
        private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
        
        ......
    
        // 從 mTokenMap 取出對應于 IBinder 的 WindowToken 對象
        WindowToken getWindowToken(IBinder binder) {
            return mTokenMap.get(binder);
        }
    
        // 在創建 WindowToken 對象時,會通過此方法將 WindowToken 從屬于此 DisplayContent 對象,并添加到 mTokenMap 中
        /** Changes the display the input window token is housed on to this one. */
        void reParentWindowToken(WindowToken token) {
            final DisplayContent prevDc = token.getDisplayContent();
            if (prevDc == this) {
                return;
            }
            if (prevDc != null && prevDc.mTokenMap.remove(token.token) != null
                && token.asAppWindowToken() == null) {
                // Removed the token from the map, but made sure it's not an app token before removing
                // from parent.
                token.getParent().removeChild(token);
            }
    
            addWindowToken(token.token, token);
        }
    
        private void addWindowToken(IBinder binder, WindowToken token) {
            final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
            if (dc != null) {
                // We currently don't support adding a window token to the display if the display
                // already has the binder mapped to another token. If there is a use case for supporting
                // this moving forward we will either need to merge the WindowTokens some how or have
                // the binder map to a list of window tokens.
                throw new IllegalArgumentException("Can't map token=" + token + " to display="
                    + getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
            }
            if (binder == null) {
                throw new IllegalArgumentException("Can't map token=" + token + " to display="
                    + getName() + " binder is null");
            }
            if (token == null) {
                throw new IllegalArgumentException("Can't map null token to display="
                    + getName() + " binder=" + binder);
            }
    
            mTokenMap.put(binder, token);
    
            if (token.asAppWindowToken() == null) {
                // Add non-app token to container hierarchy on the display. App tokens are added through
                // the parent container managing them (e.g. Tasks).
                switch (token.windowType) {
                    case TYPE_WALLPAPER:
                        mBelowAppWindowsContainers.addChild(token);
                        break;
                    case TYPE_INPUT_METHOD:
                    case TYPE_INPUT_METHOD_DIALOG:
                        mImeWindowsContainers.addChild(token);
                        break;
                    default:
                        mAboveAppWindowsContainers.addChild(token);
                        break;
                }
            }
        }
    
        // 通過此方法,將 IBinder 所對應的 WindowToken 對象從此 DisplayContent 中的 mTokenMap 移除
        WindowToken removeWindowToken(IBinder binder) {
            final WindowToken token = mTokenMap.remove(binder);
            if (token != null && token.asAppWindowToken() == null) {
                token.setExiting();
            }
            return token;
        }
    
        ......
    
    }
    
  • 代碼 2 處,若該 IBinder 對象對應的 WindowToken 不為空,則返回,可見 一個 IBinder 對象只能創建一個對應的 WindowToken 對象。
  • 代碼 3 處,根據 Window 的 type 類型創建對應的 WindowToken 對象。

AppWindowToken 是 WindowToken 的子類,與 WindowToken 不同的是,AppWindowToken 只可以用于 Activity 中的 Window 的 WindowToken。

1.2.4 DisplayContent

DisplayContent 是 Android 4.2 中支持 WiFi Display 多屏幕顯示提出的一個概念,一個 DisplayContent 對象就代表著一塊屏幕信息,一個 DisplayContent 對象用一個整型變量作為其 ID,系統默認屏幕所對應的 DisplayContent 對象 ID 是 Display.DEFAULT_DISPLAY。

屬于同一個 DisplayContent 對象的 Window 對象會被繪制到同一塊屏幕上,在添加窗口時可以指定對應的 DisplayContent 的 id,從而指定被添加到哪個 DisplayContent 上面。

DisplayContent 對象是由 DisplayManagerService 統一管理的,在此只做概念性的介紹,詳細的關于 DisplayContent 和 DisplayManagerService 知識請查閱相關文檔和資料

二. WMS 與 WindowManager 的通信

在上篇文章 初步理解 Window 體系 中,我們最后分析到了 ViewRootImpl,ViewRootImpl 是連接 WindowManager 和 WMS 的橋梁,自然他們之間的通信也是通過 ViewRootImpl 完成的。

2.1 ViewRootImpl 的成員變量

在 ViewRootImpl 中有兩個個非常重要的成員變量:mWindowSessionmWindow,這兩個變量都是用于 ViewRootImpl 和 WMS 通信使用的

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    ......
    final IWindowSession mWindowSession;
    final W mWindow;
    ......

    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        // 代碼 1,通過 WindowManagerGlobal.getWindowSession() 方法得到一個 IWindowSession 對象
        mWindowSession = WindowManagerGlobal.getWindowSession();   
        ......
        // 代碼 2,通過 W 構造方法直接創建一個新的 W 對象
        mWindow = new W(this);                                     
        ......
    }

    ......
}
2.1.1 IWindowSession

IWindowSession 是一個 AIDL 接口,其服務端進程是 WMS,客戶端進程是應用進程,IWindowSession 的創建是在 WindowManagerGlobal 中,如下所示:

    public final class WindowManagerGlobal {

        private static IWindowManager sWindowManagerService;
        private static IWindowSession sWindowSession;
        ......
        public static IWindowManager getWindowManagerService() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowManagerService == null) {
                    sWindowManagerService = IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"));
                    try {
                        if (sWindowManagerService != null) {
                            ValueAnimator.setDurationScale(
                                    sWindowManagerService.getCurrentAnimatorScale());
                        }
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowManagerService;
            }
        }

        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) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowSession;
            }
        }
        ......
    }
  • getWindowSession() 方法中我們可以看出,IWindowSession 對象的創建依賴于 IWindowManager 對象
  • IWindowManager 也是一個 AIDL 接口,通過 getWindowManagerService() 方法得到其對象,在 getWindowManagerService() 方法中,可以看到是典型的 Android 中 Binder 通信得到服務端在客戶端進程中的代理對象的方式,遠程端的對象即是 WMS,WMS 實現了 IWindowManager 接口
    public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor,     WindowManagerPolicy.WindowManagerFuncs {
        ......
    }
    
  • getWindowSession() 方法中,我們可以看到是調用了 IWindowManager 的 openSession 方法,其實際的實現是在 WMS 中,WMS 中的 openSession 方法如下所示
    public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor,     WindowManagerPolicy.WindowManagerFuncs {
    
        @Override
        public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
            if (client == null) throw new IllegalArgumentException("null client");
            if (inputContext == null) throw new IllegalArgumentException("null inputContext");
            Session session = new Session(this, callback, client, inputContext);
            return session;
        }
    }
    

可以看到,其實 ViewRootImpl 中的 IWindowSession 對象實際對應著 WMS 中的 Session 對象。

WindowManagerGlobal 和 WMS 實現的是單方向的通信,都是通過如下圖所示的 Binder 方式進行進程間通信的


WindowManagerGlobal.png
2.1.2 W

W 類是 ViewRootImpl 的一個內部類,實現了 IWindow 接口,IWindow 也是一個 AIDL 接口,可以猜想到,IWindow 接口是供 WMS 使用的,WSM 通過調用 IWindow 一些方法,通過 Binder 通信的方式,最后執行到了 W 中對應的方法中

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {


    static class W extends IWindow.Stub {
        private final WeakReference<ViewRootImpl> mViewAncestor;
        private final IWindowSession mWindowSession;

        W(ViewRootImpl viewAncestor) {
            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
            mWindowSession = viewAncestor.mWindowSession;
        }

        @Override
        public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
                boolean alwaysConsumeNavBar, int displayId) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                        visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
            }
        }

        @Override
        public void moved(int newX, int newY) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchMoved(newX, newY);
            }
        }

        @Override
        public void dispatchAppVisibility(boolean visible) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchAppVisibility(visible);
            }
        }

        @Override
        public void dispatchGetNewSurface() {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.dispatchGetNewSurface();
            }
        }

        @Override
        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
            }
        }

        private static int checkCallingPermission(String permission) {
            try {
                return ActivityManager.getService().checkPermission(
                        permission, Binder.getCallingPid(), Binder.getCallingUid());
            } catch (RemoteException e) {
                return PackageManager.PERMISSION_DENIED;
            }
        }
        
        ......

    }
}

比如在 ViewRootImpl#setView 方法中,有如下代碼,在代碼 1 處通過 mWindowSession 調用 addToDisplay 方法時,會將 mWindow 傳入,最后傳給 WMS,這樣 WMS 便得到了一個 W 對象的實例對象。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {

                ......

                int res; /* = WindowManagerImpl.ADD_OKAY; */

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 代碼 1
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }

                ......

            }
        }
    }
}

從上面代碼可以看到,在 ViewRootImpl 中不僅實現了從 ViewRootImpl 向 WMS 的通信,也實現了從 WMS 向 ViewRootImpl 的通信,如下圖所示


ViewRootImpl.png
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容