从源码角度看WMS

简介

因为我以前接触APP层的视图绘制相关业务比较多,对View的显示相关源码相对来说更感兴趣,所以想写WMS相关的源码解析很久了,但是一直迟迟没有开始写。主要是因为WMS的确是属于Android系统中最复杂的组件之一,知识点涉及从Activity生命周期、Binder、JNI、Native再到SurfaceFlinger等方方面面,分析起来容易陷入无形的大网中迷失方向

之前我有分析过View视图的测量、布局、绘制与Activity的联系,也分析过ViewRootImpl在App客户端的地位,其实就是为了这篇做准备,现在感觉基础的知识点已经具备了。但是一篇博客的内容实在是太难覆盖WMS相关的知识点了, 所以我对其中的代码进行了一定的删减,避免陷入阅读源码细节的囧境

我的目的本身是为了理解WMS在视图绘制的角色,本文中尽量多画图,少列举代码,方便快速的理解App, WMS与SurfaceFlinger的交互模式与流程

设计图

架构图

视图绘制流程:

  1. AMS binder call到应用,Activity到resume的生命周期
  2. Activity调用WMSGlobal,委托ViewRootImpl进行addWindow操作
  3. ViewRootImpl binder call到system_server到Session服务
  4. Session将请求委托给WMS,创建WindowToken, WindowState
  5. ViewRootImpl首次调用performTraverals进行窗口刷新
  6. ViewRootImpl调用到WMS,创建SurfaceControl
  7. WMS binder call到SurfaceFlinger进行Layer, producer, consumer, bufferqueue的初始化
  8. SurfaceControl返回Surface给App客户端
  9. App客户端调用View.onDraw方法进行绘制
  10. ViewRootImpl通过Surface的producer句柄binder call到SurfaceFlinger进行绘制

类图

  • ViewRootImpl: 应用进程中视图相关管理类
  • WindowManagerGlobal: 应用进程中WMS相关管理类
  • Surface: 视图的封装,本质上是和SurfaceFlinger通信的"钥匙"
  • WMS: 用于组织安卓系统中不同视图的层级、动画、大小等,并不参与绘制
  • SurfaceControl: 用于创建、获取、设置Surface
  • SurfaceFlinger: 安卓系统中真正进行绘制的进程服务
  • BufferQueueProducer: 绘制生产者
  • BufferQueueConsumer: 绘制消费者
  • BufferQueue: 绘制队列
  • Layer: 在SurfaceFlinger服务中的窗口表示类

新建窗口: WMS.addWindow

frameworks/base/core/java/android/app/ActivityThread.java

final void handleResumeActivity(IBinder token,
                                boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ...
    if (r.activity.mVisibleFromClient) {
        r.activity.makeVisible();
    }
    ...
}

关于窗口的新建与添加,我直接跳过了Activity的启动流程,直接从AMS调用scheduleResumeActivity到App端后讲起,当Activity可见时,回调用对应Activity的makeVisible方法进行可视化

frameworks/base/core/java/android/app/Activity.java

void makeVisible() {
    // 如果窗口之前并没有被添加,那么先获取WindowManager服务进行添加
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

frameworks/base/core/java/android/view/WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params,
                    Display display, Window parentWindow) {
    ...
    // App端视图相关的大管家是ViewRootImpl,一个窗口会对应一个,这里进行新建
    root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    // 保存在列表中
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);

    // 这里进行视图设置相关操作
    try {
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        ...
    }
}

和ViewRootImpl的控制器角色类似,WindowManagerGlobal是窗口相关的控制器,专门用来协调窗口相关的操作

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                      getHostVisibility(), mDisplay.getDisplayId(),
                                      mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                      mAttachInfo.mOutsets, mInputChannel);
    ...
}

ViewRootImpl并没有和WindowManagerService直接进行通信,而是通过运行在system_server的WindowSession服务进行了中转

frameworks/base/services/core/java/com/android/server/wm/Session.java

@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进行处理

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
                     WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
                     Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
                     InputChannel outInputChannel) {
    ...
    // 尝试获取token
    WindowToken token = mTokenMap.get(attrs.token);
    if (token == null) {
        // 如果为空,那么会新建一个WindowToken
        token = new WindowToken(this, attrs.token, -1, false);
    }
    ...
    // 新创建一个WindowState
    WindowState win = new WindowState(this, session, client, token,
                                          attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
    ...
    // 进行保存
    mWindowMap.put(client.asBinder(), win);
    ...
}

至此,创建视图的大概操作已经完成,addWindow的操作主要在于初始化WindowToken, WindowState这两个数据结构,WMS正是主要凭借着它们来组织视图的系统层级结构的

刷新窗口: WMS.relayoutWindow

frameworks/base/core/java/android/view/ViewRootImpl.java

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        ...
            mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...
    }
}

窗口的刷新是由VSYNC进行控制的,主要是通过调用scheduleTraversals方法进行实现,这里可以看到,主要是通过mChoreographer发送了一个Message进行实现的

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

void doTraversal() {
    if (mTraversalScheduled) {
        ...
        performTraversals();
        ...
    }
}

private void performTraversals() {
    ...
    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    ...
}

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException {
     ...
     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, mPendingConfiguration,
                mSurface);
     ...
}

TraversalRunnable这个类中,主要是进行doTraversal的调用,最终同样是通过mWindowSession binder call到system_server进行处理

frameworks/base/services/core/java/com/android/server/wm/Session.java

public int relayout(IWindow window, int seq...) {
        int res = mService.relayoutWindow(this, window, seq, attrs,
                requestedWidth, requestedHeight, viewFlags, flags,
                outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                outStableInsets, outsets, outBackdropFrame, outConfig, outSurface);
        return res;
}

Session的调用仅仅是起到了中转的作用

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int relayoutWindow(Session session, IWindow client...) {
    ...
        // 首先获取到对应的WindowState
        WindowState win = windowForClientLocked(session, client, false);
    ...
        WindowStateAnimator winAnimator = win.mWinAnimator;
        // 随后创建Surface
        result = createSurfaceControl(outSurface, result, win, winAnimator);
}

private int createSurfaceControl(Surface outSurface, int result, WindowState win, WindowStateAnimator winAnimator) {
    ...
    // 创建Surface
    WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked();
    if (surfaceController != null) {
        // 进行拷贝
        surfaceController.getSurface(outSurface);
    } else {
        // For some reason there isn't a surface.  Clear the
        // caller's object so they see the same state.
        outSurface.release();
    }
    return result;
}

Surface的创建其实是层层委托,最终交给了SurfaceControl去创建Surface

frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java

public WindowSurfaceController(SurfaceSession s,
                               String name, int w, int h, int format, int flags, WindowStateAnimator animator) {
    ...
        if (animator.mWin.isChildWindow() &&
            animator.mWin.mSubLayer < 0 &&
            animator.mWin.mAppToken != null) {
            ...
        } else if (DEBUG_SURFACE_TRACE) {
            ...
        } else {
            mSurfaceControl = new SurfaceControl(
                s, name, w, h, format, flags);
        }
}

frameworks/base/core/java/android/view/SurfaceControl.java

public SurfaceControl(SurfaceSession session,
            String name, int w, int h, int format, int flags)
                    throws OutOfResourcesException {
    ...
    mNativeObject = nativeCreate(session, name, w, h, format, flags);
    ...
}
top Created with Sketch.