android12 WindowManagerService窗口的布局过程

WindowManagerService的窗口布局过程,是在窗口完成"添加"addWindow 之后,确定其最终在屏幕上的尺寸、位置和层级(Z-Order)的关键环节 。这个过程主要由应用进程的 ViewRootImpl 触发,核心实现在WMS的 relayoutWindowperformLayout 等方法中。

触发时机:何时开始布局?

窗口布局的触发时机很灵活,每当窗口属性发生变化时都可能触发。最常见的场景是:

  • 应用首次添加窗口后 :在 ViewRootImpl.setView() 中会调用 requestLayout(),通过 Choreographer 发起一个异步遍历任务 mTraversalRunnable
  • 窗口属性变化时 :比如应用因键盘弹出、屏幕旋转等调用 requestLayout() 重新布局。
scss 复制代码
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
              int userId) {
        synchronized (this) {
                 ...
                 //请求首次布局
                 requestLayout();
                 ...
           
              }
         }
}

//frameworks/base/core/java/android/view/ViewRootImpl.java
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        ...
        scheduleTraversals();
    }
}

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //MessageQueue 中插入一个同步屏障 。这个屏障的作用是:
        //所有同步消息都不会被执行,只有异步消息可以绕过屏障继续执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //注册一个执行 View 的测量、布局、绘制的CallBack
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...
    }
}

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

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        //移除同步屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        ...
        //核心绘制流程
        performTraversals();
        ...
    }
}

跨进程请求:从应用到WMS (relayoutWindow)

mTraversalRunnable 的核心是执行 performTraversals(),其中会通过 mWindowSession.relayout() 跨进程调用WMS的 relayoutWindow 方法,携带应用期望的窗口尺寸等参数。

csharp 复制代码
//frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
    ...
    final boolean wasReportNextDraw = mReportNextDraw;
    if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
            || mForceNextWindowRelayout) {
        mForceNextWindowRelayout = false;
        ...
        try {
            ...
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            ...
        
    } else {
        ...
        maybeHandleWindowMove(frame);
    }
    ...
    mIsInTraversal = false;
}

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {
    ...
    //调用WMS.relayoutWindow()
    int relayoutResult = mWindowSession.relayout(mWindow, params,
         (int) (mView.getMeasuredWidth() * appScale + 0.5f),
         (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
         insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,   frameNumber,
        mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
        mTempControls, mSurfaceSize);
    mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
    //relayoutWindow之后填充Surface
    if (mSurfaceControl.isValid()) {
         if (!useBLAST()) {
              mSurface.copyFrom(mSurfaceControl);
          } else {
             if (blastSurface != null) {
               mSurface.transferFrom(blastSurface);
              }
         }
     }
   ... 
   
}
 

WMS核心处理:计算与分配

relayoutWindow 是WMS处理布局请求的入口,主要完成:

  1. 查找窗口状态 :通过 windowForClientLocked() 找到对应的 WindowState 对象。

  2. 触发全面布局计算 :调用 WindowSurfacePlacer.performSurfacePlacement()。这是核心步骤,内部会:

    • 执行 performLayout() :遍历所有窗口,分别处理非附加窗口附加窗口 (如PopupWindow),通过 WindowManagerPolicy.layoutWindowLw() 为每个窗口计算并保存最终的 mFrame 等坐标信息。
    • 更新窗口层级 :通过 RootWindowContainer 管理所有窗口(DisplayContent)的层级关系。
  3. 创建或更新SurfacerelayoutWindow 会为可见窗口调用 WindowStateAnimator.createSurfaceLocked() 创建 SurfaceControl。最终通过 mSurface.copyFrom(mSurfaceControl)Surface 传回给应用进程。

  4. 返回计算结果 :将计算好的窗口大小、位置等数据通过 relayout()out 参数传回给应用。

ini 复制代码
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
 public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
              int requestedWidth, int requestedHeight, int viewVisibility, int flags,
              long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
              SurfaceControl outSurfaceControl, InsetsState outInsetsState,
              InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
          ...
          synchronized (mGlobalLock) {
          //根据 `client` 查找对应的 `WindowState`,若找不到则直接返回
              final WindowState win = windowForClientLocked(session, client, false);
              if (win == null) {
                  return 0;
              }
              ...
  
              WindowStateAnimator winAnimator = win.mWinAnimator;
              if (viewVisibility != View.GONE) {
              //设置窗口大小为客户端需要的大小
                  win.setRequestedSize(requestedWidth, requestedHeight);
              }
              //`frameNumber` 用于同步帧的序列号(与 BLAST 同步机制相关)
              win.setFrameNumber(frameNumber);
  
              int attrChanges = 0;
              int flagChanges = 0;
              int privateFlagChanges = 0;
              if (attrs != null) {
                  //根据系统状态(如是否锁屏、是否在沉浸模式)调整窗口参数
                  displayPolicy.adjustWindowParamsLw(win, attrs);
                  win.mToken.adjustWindowParams(win, attrs);
                  int disableFlags =
                          (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
                  if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
                      disableFlags = 0;
                  }
                  win.mDisableFlags = disableFlags;
                  
               ...
 
              //记录可见性变化,用于后续判断是否需要移动IME或壁纸
              final int oldVisibility = win.mViewVisibility;
  
             
              final boolean becameVisible =
                      (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                              && viewVisibility == View.VISIBLE;
              boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                      || becameVisible;
              boolean focusMayChange = win.mViewVisibility != viewVisibility
                      || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                      || (!win.mRelayoutCalled);
  
              boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                      && win.hasWallpaper();
              wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
              if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
                  winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
              }
  
              win.mRelayoutCalled = true;
              win.mInRelayout = true;
  
              win.setViewVisibility(viewVisibility);
           
  
  
              win.setDisplayLayoutNeeded();
              win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
  
              
      
     //只有当窗口可见且关联的 Activity 可见时,才真正执行relayout(创建/更新 Surface)
              final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                      (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                              || win.mActivityRecord.isClientVisible());
  
              
              if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
                  
                  }
                  result |= RELAYOUT_RES_SURFACE_CHANGED;
                  if (!win.mWillReplaceWindow) {
                     
                      if (wallpaperMayMove) {
                          displayContent.mWallpaperController.adjustWallpaperWindows();
                      }

         //如果窗口不可见但已有 Surface,且没有正在播放退出动画,则尝试启动退出动画
         //启动退出动画后,Surface 会在动画结束后被释放
                      focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
                  }
              }
  
     
              if (shouldRelayout) {
                  try {
                  //创建 Surface
                      result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                  } catch (Exception e) {
                     ...
                      return 0;
                  }
              }
  
              // We may be deferring layout passes at the moment, but since the client is interested
              // in the new out values right now we need to force a layout.
              
               //####重点
              //遍历整个窗口树,进行最终的层级计算、位置确定和 Surface 显示
              mWindowPlacerLocked.performSurfacePlacement(true /* force */);
  
              if (shouldRelayout) {
                  
   //标记 `mHasSurface = true`
   //如果窗口之前是隐藏的,设置 `mFirstTimeVisible = true`
   //更新窗口的显示状态
   //如果是输入法窗口且尚未注册,立即注册
                  result = win.relayoutVisibleWindow(result);
  
                  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;
                  }
                  win.adjustStartingWindowFlags();
                  Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
              } else {
                 ...
  
                  if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
                  //可见但没有执行 relayout(如 Activity 不可见),返回现有 Surface
                      winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
                    
                  } else {
                     
                        ...
                      try {
                         
                        // 窗口不可见,释放 Surface
                          outSurfaceControl.release();
                      } finally {
                          ...
                      }
                  }
  
                 
              }
  
              if (focusMayChange) {
                  //更新焦点窗口
                  if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                      imMayMove = false;
                  }
              }
  
              // updateFocusedWindowLocked() already assigned layers so we only need to
              // reassign them at this point if the IM window state gets shuffled
              boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
              //更新输入法窗口
              if (imMayMove) {
                  displayContent.computeImeTarget(true /* updateImeTarget */);
                  if (toBeDisplayed) {
                      //IME 目标窗口可能因可见性或焦点变化而改变
                      displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                  }
              }
  
              if (wallpaperMayMove) {
              //壁纸可能需要因窗口显示/隐藏而重新调整层级
                  displayContent.pendingLayoutChanges |=
                          WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
              }
  
              if (win.mActivityRecord != null) {
                  displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord);
              }
  
              Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
              configChanged = displayContent.updateOrientation();
              Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
  
              final DisplayInfo rotatedDisplayInfo =
                      win.mToken.getFixedRotationTransformDisplayInfo();
                      
               //处理屏幕旋转
              if (rotatedDisplayInfo != null) {
               //告诉 SurfaceFlinger 该窗口的 Buffer 应该如何旋转 outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation);
              } else {
                       outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation);
              }
  
          
             ...
          }
  
          Binder.restoreCallingIdentity(origId);
          return result;
}
   
  

应用进程接收:执行View绘制

应用进程的 ViewRootImpl 收到WMS返回的结果后,会继续执行 performTraversals() 的后续步骤:

  • performMeasure() :根据WMS计算的尺寸,测量View树中每个View的大小。
  • performLayout() :根据测量结果,为View树中的每个View确定在窗口内的位置。
  • performDraw() :利用WMS返回的 Surface,将绘制内容渲染到缓冲区。

总而言之,WMS窗口布局的本质是一个从应用到系统服务,再回到应用的闭环过程。应用发起请求,WMS集中计算和分配资源,最后应用根据结果完成UI的绘制与渲染。

相关推荐
杉氧3 小时前
状态管理变迁史:为什么我们放弃了 Redux 选择 Zustand?
android·前端·react native
三少爷的鞋6 小时前
别再靠 Code Review 守底线:我做了一个静态分析项目 RedLine
android
y = xⁿ14 小时前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习
2601_9620664916 小时前
【Sql Server】Update中的From语句,以及常见更新操作方式
android·java·数据库
用户69371750013841 天前
曾经安卓开发人手一个的 EventBus,为什么现在没人爱用了?
android·android studio
九皇叔叔1 天前
MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引
android·adb
OriginCoding1 天前
用 AI Agent 协作完成一个 Android TOTP 应用:从需求边界到 v1.0.0
android·ai编程
杉氧1 天前
React 灵魂:深入剖析 Hooks 与副作用(Side Effects)陷阱
android·前端·react native
又见情义1 天前
RK3568 + RTL8211F 网络唤醒(WOL)功能适配全记录
android·网络·驱动开发