前言

WindowManager虽然在平常开发中用的不多,但是它却是一个非常重要的类,此模块管理着 Android 中所有的窗口展示,包括我们熟悉的 Activity 、Dialog 等视图。所有需要显示到屏幕上的内容都是通过 WindowManager 来实现的。此文只是一个基础入门,主要是讲解 WindowManager 和 WindowManagerService(简称 WMS)。

关系图

\"在这里插入图片描述\"

入口

WindowManager 的创建

先放结果
\"在这里插入图片描述\"

通过字面理解 WindowManager 是一个 Manager,那么它在 Android 源码中也是以容器单例模式的形式,以键-值方式存储的,在 SystemServiceRegistry 我们可以找到 WidowManager 的创建过程

     registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx.getDisplay());
            }});

显而易见的,WindowManager 的具体实现类是 WindowManagerImpl。
那么 WindowManagerImpl 又是如何运行的呢?
这个我们还是得回到最开始的调用的部分,此处就以 Activity 的 mWindow , Activity 的 Window 的是在 attach 创建的,此处我就不多介绍了

  final void attach(....) {

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
      ...

        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();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
    }

WindowManager 与 Window 是通过此行代码进行相关联的
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(),

此处就是真正的入口了,进入 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);
    }

到此处终于发现了核心关联代码,就是最后一句代码了,此方法内部只进行了创建了 WindowManagerImpl 对象,需要注意的是此处是带了具体参数的,表明当前 Window 与 WindowManagerImpl 一一对应。依旧直接说结果
\"在这里插入图片描述\"


    public WindowManagerImpl(Context context) {
        this(context, null);
    }

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

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

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return new WindowManagerImpl(displayContext, mParentWindow);
    }

Window 的 add 过程

在此类中我们查看了几个核心放,比如 addView、removeView、updateViewLayout 等方法,发现都是去 WindowManagerGlobal 实现的,那就接着看呗。这里实现的是一个简易工厂模式,大家可以仔细体会下

    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException(\"view must not be null\");
        }
        if (display == null) {
            throw new IllegalArgumentException(\"display must not be null\");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException(\"Params must be WindowManager.LayoutParams\");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there\'s no parent, then hardware acceleration for this view is
            // set from the application\'s hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(params);
        root.setView(view, wparams, panelParentView);
            

上述方法是经过代码缩减的,留下了核心的几行代码,通过此处我们知道此函数做了哪些事情
1.对 View 进行判断
2.构建 ViewRootImpl
3.View 设置 view.setLayoutParams
4.存储 view、root、params 添加到缓存队列中。
5. ViewRootImpl 持有当前 View
在第五步中,root 其实是一个View的控制类,是 work 与 Native 的中间件,View的展示肯定是在 Native 展示的,执行 setView 会进行View的measure,layout,draw 操作。

那么我这边只需要关心下 ViewRootImpl 是和 WindowManager 怎么关联的,先看下 ViewRootImpl 的创建

 public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        m PackageName = context.get PackageName();
        //ViewRootImpl 是在UI线程创建的,这里保证了 UI线程一定是在 
        //ViewRootImpl线程中的。
        mThread = Thread.currentThread();

}

会发现 ViewRootImpl 会持有一个 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) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return 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;
        }
    }

getWindowManagerService 的获取 Service 的方式是通过 ServiceManager 类的

    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, \"error in getService\", e);
        }
        return null;
    }

返回的是一个 IBinder 对象,所以到这里我们就清楚了,getWindowManagerService 方法是为了获取 WindowManagerService ,而获取方式是通过 Binder 通信来获取的,获取得到 WMS,WMS 通过 openSession 函数来与 WMS 建立一个通道。双方的消息均是通过此处来处理的,我们直接看下 WMS 的 openSession 方法,一目了然


    // -------------------------------------------------------------
    // IWindowManager API
    // -------------------------------------------------------------

    @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;
    }

源码里面注释也写到了,**IWindowManager API ** 这个是对外的公开API。
到了这边,我们算是搞清楚了 ViewImpl-WindowManager-WMS的联系了,我们发现了WindowManager 只是一个中间类,真正联系的是 ViewImpl-WMS ,ViewImpl 持有了 mWindowSession,而 mWindowSession 是 WMS 在外部的一个代理。
现在理清楚3者的关系后(文章开头已经给出),我们就可以具体去看下 ViewRootImpl 的 setView过程了。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

 requestLayout();
  try {
        mOrigWindowType = mWindowAttributes.type;
        mAttachInfo.mRecomputeGlobalAttributes = true;
        collectViewAttributes();
         res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(),
        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);

此函数非常的复杂,其实只做了2件事情:

调用 requestLayout创建Surface, 进行 View 的绘制、测量、绘画

在开始绘制之前还会创建具体的 Surface ,创建好 Surface 后,ViewRootImpl 调用 Surface 的 lockCanvas(),得到一块画布, 就可以直接可以在 canvas 上画图,最终都会保存到 Surface 里的 buffer 里,最后由 SurfaceFlinger 合成并显示。
在 performTraversals 方法中会调用执行4个核心方法。

relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();

接着看 relayoutWindow 方法,此处的 mWindowSession 在上文已经交代过了,实际上是 Seesion ,在Session 中真正执行 WMS 的 relayoutWindow 调用 WMS 的 relayout 方法,根据Window测量的大小相对应创建出SurfaceControl。

ViewRootImpl.java:

        int relayoutResult = mWindowSession.relayout(
                mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                mWin , mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDrop ,
                mPendingMergedConfiguration, mSurface);

通过此函数,我们发现 mSurface 对应当前ViewRootImpl 通过此函数,ViewRootImpl 与 WMS 进行了通信。

WMS.java:

 public int relayoutWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, int flags,
            Rect out , Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdrop ,
            MergedConfiguration mergedConfiguration, Surface outSurface) {
            
            ...
            result = createSurfaceControl(outSurface, result, win, winAnimator);
...
            
            }

到了这边才是真正意义上去创建 Surface,Surface是什么东西呢?我们可以理解为每一个Window对应一个Surface,DecorView及其子View的绘制都是在Surface上进行的,此处的 outSurface 就是我们的 ViewRootImpl 的参数 mSurface。

private int createSurfaceControl(Surface outSurface, int result, WindowState win,
            WindowStateAnimator winAnimator) {
        if (!win.mHasSurface) {
            result |= RELAYOUT_RES_SURFACE_CHANGED;
        }

        WindowSurfaceController surfaceController;
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, \"createSurfaceControl\");
            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        if (surfaceController != null) {
            surfaceController.getSurface(outSurface);
            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, \"  OUT SURFACE \" + outSurface + \": copied\");
        }
            outSurface.release();
        }

        return result;
    }

到了此处,我们终于发现 Surface 的控制类,该控制类里面持有了本地创建的 Surface,其实里面还有很多层,就不多多赘述了,最后的创建是在 Native 层的。在创建好native的SurfaceControl后面,调用了 SurfaceController.getSurface(), SurfaceController 里保存了一个JAVA层的SurfaceControl,JAVA层的SurfaceControl持有native层的SurfaceControl。

而 surfaceController.getSurface 方法会把当前 ViewRootImpl 与本地创建的 Surface 进行了绑定了,要使用draw就必须要关联 native 的 Surface 才能在屏幕上有图像,getSurface 函数最后会进入 Surface.copyFrom 函数。

    public void copyFrom(SurfaceControl other) {
        if (other == null) {
            throw new IllegalArgumentException(\"other must not be null\");
        }
        //创建了一个native surface
        long surfaceControlPtr = other.mNative ;
        if (surfaceControlPtr == 0) {
            throw new NullPointerException(
                    \"null SurfaceControl native  . Are you using a released SurfaceControl?\");
        }
        long newNative  = nativeGetFromSurfaceControl(surfaceControlPtr);

        synchronized (mLock) {
            if (mNative  != 0) {
                nativeRelease(mNative );
            }
            //这个函数中做了Java层也native层关联
            setNative Locked(newNative );
        }
    }
   private void setNative Locked(long ptr) {
        if (mNative  != ptr) {
            if (mNative  == 0 && ptr != 0) {
                mCloseGuard.open(\"release\");
            } else if (mNative  != 0 && ptr == 0) {
                mCloseGuard.close();
            }
            mNative  = ptr;
            mGenerationId += 1;
            if (mHwuiContext != null) {
                mHwuiContext.updateSurface();
            }
        }
    }

先创建一个本地surface,然后在outSurface的对象上调用copyFrom,将本地Surface的信息拷贝到outSurface中,就将本身没有什么作用的 ViewRootImpl 中的 Surface 填充了数据。就可以使用 draw 相关的调用了。WMS的窗口设置属性和应用的ViewRootImpl最后是通过SurfaceControl和Surface的native层和SurfaceFlinger通信的
####向 WMS 发起显示当前 Window 的请求。

之前分析了 mWindowSession 是一个 WMS openSession 的返回结果,从上面可以得到是一个 Session 类。

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

此处只是一个中转,接着又回到了 WMS 的 addWindow 方法,这个方法又是非常的复杂。。。。这里我直接说结果吧,会调用 WindowState 的 attach 方法,而 WindowState 则是WMS端的Window对象,它持有Session与WindowManager通信

接着执行 Session 的 windowAddedLocked 方法:


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

到了这边,我们会发现本质是去创建一个 SurfaceSession 类

   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);
            mService.mSessions.add(this);
            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                mService.dispatchNewAnimatorScaleLocked(this);
            }
        }
        mNumWindow++;
    }
    

在 SurfaceSession 类里面其实已经到Native层了

   /** Create a new connection with the surface flinger. */
    public SurfaceSession() {
        mNativeClient = nativeCreate();
    }

SurfaceSession构造方法里调用了nativeCreate,从这里开始就是native的世界,不是本文重点,但简单概括一下流程是通过创建 SurfaceComposerClient 与SurfaceFlinger进行交互,锁定一块共享内存,通过writeParcel返回给ViewRootImpl.mSurface,同时拥有了native surface的地址。

今天的分析就到这里,有兴趣的朋友可以跟着走一遍源码,大家加油!

收藏 打印