Android窗口创建中的关键类分析

1,564 阅读7分钟

窗口的本质Surface

窗口的本质是进行绘制所使用的画布:Surface。

在Android中,Window与Surface一一对应,当一块Surface显示在屏幕上时,就是用户所看到的窗口了。客户端向WMS添加一个窗口的过程,就是WMS为其分配一块Surface的过程,一块块Surface在WMS的管理之下有序地排布在屏幕上,Android才得以呈现出各种各样的界面出来。

ViewRootImpl、 WindowManagerImpl、 WindowManagerGlobal的关系

ViewRootImpl, WindowManagerImpl, WindowManagerGlobal 三者都存在于应用(有Activity)的进程空间里,一个Activity对应一个WindowManagerImpl、 一个DecorView(ViewRoot)、以及一个ViewRootImpl,而WindowManagerGlobals是一个全局对象,一个应用永远只有一个。

截图

IWindowManager和ISession

IWindowManager: 主要接口是OpenSession(),用于在WindowManagerService内部创建和初始化Session,并返回IBinder对象。

ISession: 是Activity Window与WindowManagerService 进行对话的主要接口。

WindowManagerGlobal

WindowManagerGlobal是WindowManagerImpl中的成员mGlobal,

WindowManagerImpl的一些方法最终是通过调用WindowManagerGlobal实现。

WindowManagerImpl

WindowManagerImpl的初始化时机

发生在Activity#performLaunchActivity阶段

继续看里面的Activity#attach方法

//android.app.Activity#attach
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo inf
        o,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    ...
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    ...
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    ...
}

这里我们看初始化了一个PhoneWindow,并在setWindowManager里面初始化了WindowManagerImpl

//android.view.Window#setWindowManager
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    ...
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//android.view.WindowManagerImpl#createLocalWindowManager
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mContext, parentWindow);
}

ViewRootImpl

ViewRootImpl的创建时机

ViewRootImpl的创建发生在handleResumeActivity阶段,在这里将创建ViewRootImpl并将Window、ViewRootImpl、WindowManager三者建立联系。

    @Override
    // core/java/android/app/ActivityThread.java
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
      ...
      wm.addView(decor, l);
      ...
    }
    
    // core/java/android/view/WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow, int userId) {
      ...
      root = new ViewRootImpl(view.getContext(), display);
      view.setLayoutParams(wparams);
      mViews.add(view);
      mRoots.add(root);
      mParams.add(wparams);
      ...
      root.setView(view, wparams, panelParentView, userId);
    }

设置sync监听

在ViewRootImpl#setView()方法中,调用mWindowSession#addToDisplayAsUser()方法之前,先是调用了requestLayout()方法,并最终通过Chreographer对象设置了一个VSync监听:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
  
    ...
  // 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();
  ...
  res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
    getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
    mAttachInfo.mDisplayCutout, inputChannel,
    mTempInsets, mTempControls);
  }

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

Surface的初始化时机

ViewRootImpl里的mSurface在初始化时是空的,他将在relayoutWindow窗口布局阶段时被填充

public final Surface mSurface = new Surface();

mChoreographer

ViewRootImpl的mChoreographer成员是个单例,由第一个访问getInstance()方法的ViewRootImpl创建

  // core/java/android/view/ViewRootImpl.java
  public ViewRootImpl(Context context, Display display, IWindowSession session,
        boolean useSfChoreographer) {
    ...
    mChoreographer = useSfChoreographer
            ? Choreographer.getSfInstance() : Choreographer.getInstance();
    ...
  }

Choreographer

Choreographer里的mDisplayEventReceiver用于接收sync信号,mHandler是内部类FrameHandler的实例,用于handleMessage

FrameHandler

  // core/java/android/view/Choreographer.java
      private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
        ...
    }
    private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

doFrame()

ViewRootImpl的scheduleTraversals()方法就是向Choreographer的mCallbackQueue添加TRAVERASALS类型的callback,在doFrame时会去执行这个callback。doFrame会依次执行五次doCallback处理不同类型的callback

  void doFrame(long frameTimeNanos, int frame) {
    ...
      doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
      doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
      doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
      doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
      doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
    ...
  }

performTraversals

CALLBACK_TRAVERSAL的callback执行的就是ViewRootImpl里的TraversalRunnable,执行关键方法performTraversals()

// core/java/android/view/ViewRootImpl.java
private void performTraversals() {
      ...
      // 预测量阶段
      windowSizeMayChange |= measureHierarchy(host, lp, res,
        desiredWindowWidth, desiredWindowHeight);
      ...
      // 窗口布局阶段
      relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
      ...
      // 测量
      performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);  
      ...
      // 布局
      performLayout(lp, mWidth, mHeight);
      ...
      // 绘制
      performDraw();
      ...
    }

Surface的填充时机

ViewRootImpl里的mSurface在初始化时是空的,他将在relayoutWindow时被填充

// core/java/android/view/ViewRootImpl.java
    public final Surface mSurface = new Surface();

    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, frameNumber,
        mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
        mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
        mTempControls, mSurfaceSize, mBlastSurfaceControl);
        if (mSurfaceControl.isValid()) {
            if (!useBLAST()) {
                mSurface.copyFrom(mSurfaceControl);
            } else {
                final Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
                    mSurfaceSize.y);
                // If blastSurface == null that means it hasn't changed since the last time we
                // called. In this situation, avoid calling transferFrom as we would then
                // inc the generation ID and cause EGL resources to be recreated.
                if (blastSurface != null) {
                    mSurface.transferFrom(blastSurface);
                }
            }
        } else {
            destroySurface();
        }
    ...
    }

WindowManagerService#relayoutWindow方法源码解析

/**
     * @param session                      IWindowSession对象,用于ViewRootImpl向WMS发起交互
     * @param client                       IWindow对象,代表客户端的Window,用于WMS向ViewRootImpl发起交互
     * @param seq                          请求序列
     * @param attrs                        窗口各种参数和属性
     * @param requestedWidth               客户端请求窗口的宽
     * @param requestedHeight              客户端请求窗口的高
     * @param viewVisibility               View的可见性
     * @param flags                        标记
     * @param frameNumber
     * @param outFrame                     返回给ViewRootImpl的窗口框架
     * @param outContentInsets                
     * @param outVisibleInsets
     * @param outStableInsets
     * @param outBackdropFrame
     * @param outCutout
     * @param mergedConfiguration
     * @param outSurfaceControl            返回给ViewRootImpl的Surface管理对象
     * @param outInsetsState
     * @param outActiveControls
     * @param outSurfaceSize
     * @param outBLASTSurfaceControl
     * @return
     */
    public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, Rect outFrame, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
            SurfaceControl outBLASTSurfaceControl) {
                ......
        boolean configChanged;
        synchronized (mGlobalLock) {
            // 获取WindowState、DisplayContent、DisplayPolicy、WindowStateAnimator对象
            final WindowState win = windowForClientLocked(session, client, false);
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            WindowStateAnimator winAnimator = win.mWinAnimator;
            // 给WindowState设置来自应用请求的窗口大小
            if (viewVisibility != View.GONE) {
                win.setRequestedSize(requestedWidth, requestedHeight);
            }
            // 设置Frame number
            win.setFrameNumber(frameNumber);
        final DisplayContent dc = win.getDisplayContent();
        // 如果此时没有执行Configuration的更新,试图结束衔接动画
        if (!dc.mWaitingForConfig) {
            win.finishSeamlessRotation(false /* timeout */);
        }
        // 用来标记属性是否发生变化
        int attrChanges = 0;
        int flagChanges = 0;
        int privateFlagChanges = 0;
        int privflagChanges = 0;
        if (attrs != null) {
            // 调整特殊类型的Window#attrs属性
            displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
            // 针对壁纸窗口调整Window#attrs属性
            win.mToken.adjustWindowParams(win, attrs);
            // 调整mSystemUiVisibility属性,控制status bar的显示
            if (seq == win.mSeq) {
                int systemUiVisibility = attrs.systemUiVisibility
                        | attrs.subtreeSystemUiVisibility;
                                            ......
                }
                win.mSystemUiVisibility = systemUiVisibility;
            }

            // PRIVATE_FLAG_PRESERVE_GEOMETRY将忽略新x、y、width、height值,使用旧值
            if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
                    != 0) {
                attrs.x = win.mAttrs.x;
                attrs.y = win.mAttrs.y;
                attrs.width = win.mAttrs.width;
                attrs.height = win.mAttrs.height;
            }
            // 确定flag是否发生变化
            flagChanges = win.mAttrs.flags ^ attrs.flags;
            privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
            attrChanges = win.mAttrs.copyFrom(attrs);
            // 根据flag是否发生变化做出对应响应,略....
            .......
        }
        // 根据应用请求设置宽高,获取窗口缩放比例
        win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
        // 窗口此时的可见状态
        final int oldVisibility = win.mViewVisibility;
        // 窗口是否要由不可见状态转变为可见状态
        final boolean becameVisible =
                (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                        && viewVisibility == View.VISIBLE;
        // 是否需要移除IME窗口
        boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                || becameVisible;
                // 确定是否需要更新focus状态,第一次执行relayout时,mRelayoutCalled为false
        boolean focusMayChange = win.mViewVisibility != viewVisibility
                || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                || (!win.mRelayoutCalled);
                // 如果窗口可见性发生变化,且该窗口允许显示在壁纸之上,则对壁纸窗口进行处理
        boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
        wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
        
        win.mRelayoutCalled = true;                // 设置WindowState#mRelayoutCalled为true
        win.mInRelayout = true;                        // 设置WindowState#mInRelayout为true,表示在relayout过程中,relayout完毕后,重置为false
        // 更新窗口的可见性
        win.setViewVisibility(viewVisibility);
        // 通知DisplayContent,需要进行重新布局
        win.setDisplayLayoutNeeded();
        // 表示是否在等待设置Inset
        win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;

        // 如果窗口可见,进行重新布局
        final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                        || win.mActivityRecord.isClientVisible());
        // 执行一遍刷新操作
        mWindowPlacerLocked.performSurfacePlacement(true /* force */);
                    
        // 是否需要进行relayout操作
        if (shouldRelayout) {
            // 布局
            result = win.relayoutVisibleWindow(result, attrChanges);
            try {
                // 创建Surface
                result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
                        result, win, winAnimator);
            } catch (Exception e) {
                return 0;
            }
            // 如果是第一次relayout操作,需要focusMayChange
            if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                focusMayChange = true;
            }
            // 输入法窗口的特殊处理
            if (win.mAttrs.type == TYPE_INPUT_METHOD
                    && displayContent.mInputMethodWindow == null) {
                displayContent.setInputMethodWindowLocked(win);
                imMayMove = true;
            }
        } else {
                            ......
        }
        // 更新FocusWindow
        if (focusMayChange) {
            if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                imMayMove = false;
            }
        }
        // 是否首次更新
        boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
        // 更新屏幕显示方向
        configChanged = displayContent.updateOrientation();
        // 对壁纸窗口的特殊处理,更新偏移量
        if (toBeDisplayed && win.mIsWallpaper) {
            displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
        }
        if (win.mActivityRecord != null) {
            win.mActivityRecord.updateReportedVisibilityLocked();
        }
        ......
        // 更新mergedConfiguration对象
        if (shouldRelayout) {
            win.getMergedConfiguration(mergedConfiguration);
        } else {
            win.getLastReportedMergedConfiguration(mergedConfiguration);
        }
        // 设置各种Inset和DisplayCutout
        win.getCompatFrame(outFrame);
        win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
                outStableInsets);
        outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
        outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
        outInsetsState.set(win.getInsetsState(), win.isClientLocal());

        // 更新relayout标记,表示可接受touch事件
        result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;

        win.mInRelayout = false;                // 重置为false,表示relayout过程完成
        // configuration发生变化时,更新全局Configuration
        if (configChanged) {
            displayContent.sendNewConfiguration();
        }
        // 设置outSurfaceSize
        if (winAnimator.mSurfaceController != null) {
            outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
                                     winAnimator.mSurfaceController.getHeight());
        }
        getInsetsSourceControls(win, outActiveControls);
    }

    return result;
}

总结

一个Activity(Window)从创建到绘制出来的过程:

  1. Acitivity创建, ViewRootImpl将窗口注册到WindowManager Service(WindowManagerGlobal),WMS通过SurfaceFlinger的接口创建了一个Surface Session用于接下来的Surface管理工作。
  2. VSYNC事件到来,Choreographer会运行ViewRootImpl注册的Callback函数,这个函数会最终调用performTraversal遍历View树里的每个View。在第一个VSYNC里,WindowManager Service 会创建一个SurfaceControl 对象,ViewRootImpl 根据Parcel返回的该对象生成了Window对应的Surface对象,通过这个对象,Canvas 可以要求SurfaceFlinger分配OpenGL绘图用的Buffer。
  3. View树里的每个View 会根据需要依次执行 measure(),layout() 和 draw() 操作。Android 在3.0之后引入了硬件加速机制,为每个View生成DisplayList,并根据需要在GPU内部生成Hardware Layer,从而充分利用GPU的功能提升图形绘制速度。
  4. 当某个View发生变化,它会调用invalidate()请求重绘,这个函数从当前View 出发,向上遍历找到View Tree中所有Dirty的 View 和 ViewGroup, 根据需要重新生成DisplayList, 并在drawDisplayList() 函数里执行OpenGL命令将其绘制在某个Surface Buffer上。
  5. 最后,ViewRootImpl 调用 eglSwapBuffer 通知OpenGL 将绘制的Buffer 在下一个VSync点进行显示。

一张时序图(来源:图解Android - Android GUI 系统 (2) - 窗口管理 (View, Canvas, Window Manager)):

时序