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的绘制与渲染。

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
哈基米南北1 天前
ViewRootImpl 事件分发责任链分析
源码
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas