Android WMS、 ViewRootImpl、Surface之间的关系

前言

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的控制类,是 Framework 与 Native 的中间件,View的展示肯定是在 Native 展示的,执行 setView 会进行View的measure,layout,draw 操作。

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

 public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();
        //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,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
                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 outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
            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.mNativeObject;
        if (surfaceControlPtr == 0) {
            throw new NullPointerException(
                    "null SurfaceControl native object. Are you using a released SurfaceControl?");
        }
        long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);

        synchronized (mLock) {
            if (mNativeObject != 0) {
                nativeRelease(mNativeObject);
            }
            //这个函数中做了Java层也native层关联
            setNativeObjectLocked(newNativeObject);
        }
    }
   private void setNativeObjectLocked(long ptr) {
        if (mNativeObject != ptr) {
            if (mNativeObject == 0 && ptr != 0) {
                mCloseGuard.open("release");
            } else if (mNativeObject != 0 && ptr == 0) {
                mCloseGuard.close();
            }
            mNativeObject = 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的地址。

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

发布了73 篇原创文章 · 获赞 15 · 访问量 15万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览