1. 程式人生 > >Android WMS、 ViewRootImpl、Surface之間的關係

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的地址。

今天的分析就到這裡,有興趣的朋友可以跟著走一遍原始碼,大家加油!