Files
mkdocs/docs/android面试/系统原理/WMS面试.md
2026-01-16 14:54:07 +08:00

1475 lines
22 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WMS窗口常见面试题
本文档整理了Android WMSWindow Manager Service窗口管理相关的常见面试题涵盖基础概念、架构原理、源码分析、问题排查等多个方面。
## 目录
- [基础概念类](#基础概念类)
- [架构原理类](#架构原理类)
- [窗口管理类](#窗口管理类)
- [源码分析类](#源码分析类)
- [性能优化类](#性能优化类)
- [问题排查类](#问题排查类)
- [多窗口相关](#多窗口相关)
---
## 基础概念类
### Q1: 什么是WMS它的主要职责是什么
**答案:**
WMSWindow Manager Service是Android窗口系统的核心服务运行在system_server进程中。
**主要职责:**
1. **窗口管理**:管理所有窗口的创建、显示、销毁等生命周期
2. **窗口层级管理**维护窗口的Z-Order顺序决定窗口的显示优先级
3. **布局计算**:计算窗口的位置、大小和可见区域
4. **输入事件分发**:将触摸、按键等输入事件分发给正确的窗口
5. **窗口动画**:管理窗口的显示、隐藏、切换等动画效果
6. **屏幕旋转**:处理屏幕方向变化时的窗口布局调整
7. **多显示支持**:支持多个显示设备的窗口管理
```java
// WMS是系统服务在SystemServer中启动
public class SystemServer {
    private void startOtherServices(...) {
        WindowManagerService wm = WindowManagerService.main(...);
        ServiceManager.addService(Context.WINDOW_SERVICE, wm);
    }
}
```
### Q2: WMS与WindowManager、WindowManagerGlobal的关系是什么
**答案:**
这三者的关系如下:
1. **WindowManager**应用层接口开发者使用的API
2. **WindowManagerGlobal**应用层实现管理ViewRootImpl和窗口会话
3. **WindowManagerService**:系统服务,实际管理窗口的服务端
**调用流程:**
```
应用层WindowManager.addView()
   
WindowManagerGlobal.addView()
   
ViewRootImpl.setView()
   
IWindowSession.addToDisplay() (Binder调用)
   
WMS.addWindow() (服务端)
```
```java
// WindowManager接口
public interface WindowManager extends ViewManager {
    void addView(View view, ViewGroup.LayoutParams params);
    void removeView(View view);
    void updateViewLayout(View view, ViewGroup.LayoutParams params);
}
// WindowManagerGlobal实现
public final class WindowManagerGlobal {
    public void addView(View view, ViewGroup.LayoutParams params, ...) {
        ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
        root.setView(view, wparams, panelParentView);
    }
}
// WMS服务端
public class WindowManagerService {
    public int addWindow(Session session, IWindow client, ...) {
        // 实际窗口管理逻辑
    }
}
```
### Q3: 窗口类型有哪些?它们的层级关系是什么?
**答案:**
**窗口类型:**
1. **应用窗口APPLICATION**
   - `TYPE_APPLICATION`普通Activity窗口
   - `TYPE_APPLICATION_STARTING`:启动窗口
   - `TYPE_BASE_APPLICATION`:应用基础窗口
2. **子窗口SUB_WINDOW**
   - `TYPE_APPLICATION_PANEL`:应用面板
   - `TYPE_APPLICATION_SUB_PANEL`:应用子面板
   - `TYPE_APPLICATION_ATTACHED_DIALOG`:附加对话框
3. **系统窗口SYSTEM_WINDOW**
   - `TYPE_STATUS_BAR`:状态栏
   - `TYPE_NAVIGATION_BAR`:导航栏
   - `TYPE_SYSTEM_ALERT`:系统警告窗口
   - `TYPE_TOAST`Toast窗口
   - `TYPE_APPLICATION_OVERLAY`:悬浮窗(需要权限)
**层级关系Z-Order**
```
系统窗口(最高)
   
应用窗口(中等)
   
子窗口(最低)
```
```java
// 窗口类型定义
public static class LayoutParams {
    // 应用窗口1000-1999
    public static final int TYPE_APPLICATION = 2;
    public static final int TYPE_APPLICATION_STARTING = 3;
    // 子窗口1000-1999
    public static final int TYPE_APPLICATION_PANEL = 1000;
    // 系统窗口2000-2999
    public static final int TYPE_STATUS_BAR = 2000;
    public static final int TYPE_SYSTEM_ALERT = 2003;
    public static final int TYPE_TOAST = 2005;
    public static final int TYPE_APPLICATION_OVERLAY = 2038;
}
```
---
## 架构原理类
### Q4: WMS的架构是怎样的包含哪些核心组件
**答案:**
**WMS架构分层**
```
┌─────────────────────────────────┐
│  应用层(客户端)                  │
 Activity/WindowManager          │
└──────────────┬──────────────────┘
               │ Binder IPC
┌──────────────▼──────────────────┐
│  系统服务层(服务端)              │
 WindowManagerService            │
│  ├── WindowState                
│  ├── DisplayContent              │
│  ├── WindowToken                
│  └── WindowAnimator              │
└──────────────┬──────────────────┘
               │
┌──────────────▼──────────────────┐
│  显示层                          │
 SurfaceFlinger                
└─────────────────────────────────┘
```
**核心组件:**
1. **WindowManagerService**WMS主类管理所有窗口
2. **WindowState**:窗口状态,表示一个窗口的所有信息
3. **DisplayContent**:显示内容,管理一个显示设备上的所有窗口
4. **WindowToken**:窗口令牌,用于窗口分组和权限控制
5. **WindowAnimator**:窗口动画控制器
6. **WindowStateAnimator**:单个窗口的动画状态
```java
// WindowState窗口状态
class WindowState {
    final WindowManagerService mService;
    final WindowToken mToken;
    final WindowManager.LayoutParams mAttrs;
    final DisplayContent mDisplayContent;
    SurfaceControl mSurfaceControl;
    // 窗口属性
    int mLayer;  // 层级
    Rect mFrame; // 位置和大小
    boolean mVisible; // 是否可见
}
```
### Q5: 窗口添加的完整流程是什么?
**答案:**
**窗口添加流程:**
```
1. Activity.attach()
   └──> 创建PhoneWindow
2. Activity.onCreate()
   └──> setContentView()
       └──> PhoneWindow.setContentView()
           └──> 创建DecorView
3. Activity.onResume()
   └──> Activity.makeVisible()
       └──> WindowManager.addView()
           └──> WindowManagerGlobal.addView()
               └──> 创建ViewRootImpl
                   └──> ViewRootImpl.setView()
                       └──> IWindowSession.addToDisplay()
                           └──> WMS.addWindow()
                               ├──> 创建WindowState
                               ├──> 创建Surface
                               └──> 更新布局
```
**关键代码:**
```java
// 1. Activity创建窗口
public final void attach(...) {
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowManager(
        (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        mToken, mComponent.flattenToString(),
        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
}
// 2. WMS添加窗口
public int addWindow(Session session, IWindow client, ...) {
    // 权限检查
    if (!checkCallingPermission(...)) {
        return WindowManagerGlobal.ADD_PERMISSION_DENIED;
    }
    // 创建WindowState
    WindowState win = new WindowState(this, session, client, ...);
    // 添加到DisplayContent
    DisplayContent displayContent = getDisplayContent(displayId);
    displayContent.addWindow(win);
    // 创建Surface
    win.createSurfaceControl();
    // 更新布局
    performLayoutAndPlaceSurfacesLocked();
    return WindowManagerGlobal.ADD_OKAY;
}
```
### Q6: WMS如何管理窗口的Z-Order层级
**答案:**
**Z-Order管理机制**
1. **基础层级**由窗口类型决定TYPE值
2. **层级调整**:通过`WindowState.mLayer`动态计算
3. **层级排序**DisplayContent维护窗口列表按层级排序
**层级计算:**
```java
// WMS计算窗口层级
void assignWindowLayers(DisplayContent displayContent) {
    int baseLayer = 0;
    // 1. 系统窗口:最高层级
    for (WindowState win : systemWindows) {
        win.mLayer = baseLayer + win.mAttrs.type;
    }
    // 2. 应用窗口:中等层级
    for (WindowState win : applicationWindows) {
        win.mLayer = baseLayer + win.mAttrs.type;
    }
    // 3. 子窗口:最低层级(相对于父窗口)
    for (WindowState win : childWindows) {
        win.mLayer = win.mParentWindow.mLayer + win.mAttrs.type;
    }
    // 4. 按层级排序
    displayContent.sortWindowsByLayer();
}
```
**影响层级的因素:**
- 窗口类型TYPE
- 窗口标志FLAG
- 窗口动画状态
- 窗口可见性
---
## 窗口管理类
### Q7: WindowState的作用是什么它包含哪些关键信息
**答案:**
**WindowState的作用**
WindowState是WMS中表示一个窗口状态的核心类包含了窗口的所有信息。
**关键信息:**
1. **窗口属性**LayoutParams、类型、标志等
2. **窗口状态**:可见性、焦点状态、动画状态等
3. **窗口位置**Frame、可见区域、裁剪区域等
4. **Surface信息**SurfaceControl、Surface等
5. **窗口关系**父窗口、子窗口、Token等
```java
class WindowState {
    // 窗口属性
    final WindowManager.LayoutParams mAttrs;
    final WindowToken mToken;
    final DisplayContent mDisplayContent;
    // 窗口状态
    boolean mVisible;
    boolean mHasSurface;
    boolean mIsAnimating;
    // 窗口位置
    Rect mFrame;           // 窗口位置和大小
    Rect mVisibleFrame;     // 可见区域
    Rect mContentFrame;    // 内容区域
    // Surface
    SurfaceControl mSurfaceControl;
    Surface mSurface;
    // 窗口关系
    WindowState mParentWindow;
    ArrayList<WindowState> mChildWindows;
    // 层级
    int mLayer;
}
```
### Q8: DisplayContent的作用是什么它如何管理窗口
**答案:**
**DisplayContent的作用**
DisplayContent表示一个显示设备上的所有窗口内容管理该显示设备上的窗口栈、布局和显示。
**主要功能:**
1. **窗口管理**:维护该显示设备上的所有窗口
2. **窗口栈管理**维护窗口的Z-Order顺序
3. **布局计算**:计算窗口的位置和大小
4. **显示配置**:管理显示设备的分辨率、方向等
```java
class DisplayContent {
    final int mDisplayId;
    final Display mDisplay;
    // 窗口列表
    final WindowList mWindows = new WindowList();
    // 窗口栈
    final ArrayList<TaskStack> mStacks = new ArrayList<>();
    // 显示配置
    Configuration mDisplayConfiguration;
    // 添加窗口
    void addWindow(WindowState win) {
        mWindows.add(win);
        assignWindowLayers();
    }
    // 布局计算
    void performLayout() {
        for (WindowState win : mWindows) {
            computeWindowFrame(win);
        }
    }
}
```
### Q9: WindowToken的作用是什么为什么需要WindowToken
**答案:**
**WindowToken的作用**
WindowToken是窗口的令牌用于
1. **窗口分组**:将属于同一应用的窗口分组
2. **权限控制**控制哪些窗口可以添加到Token
3. **窗口关联**关联Activity、Service等组件
**为什么需要WindowToken**
1. **安全性**:防止其他应用随意添加窗口到你的应用
2. **窗口管理**:统一管理同一组件的所有窗口
3. **生命周期**Token销毁时所有关联窗口都会被移除
```java
class WindowToken {
    final IBinder token;  // Token标识
    final int windowType; // 窗口类型
    final String name;    // Token名称
    // 关联的窗口列表
    final ArrayList<WindowState> windows = new ArrayList<>();
    // 添加窗口
    void addWindow(WindowState win) {
        windows.add(win);
    }
    // 移除窗口
    void removeWindow(WindowState win) {
        windows.remove(win);
        if (windows.isEmpty()) {
            // Token可以销毁
        }
    }
}
```
---
## 源码分析类
### Q10: WMS的addWindow方法做了哪些事情
**答案:**
**addWindow方法流程**
```java
public int addWindow(Session session, IWindow client, ...) {
    // 1. 权限检查
    if (!checkCallingPermission(...)) {
        return WindowManagerGlobal.ADD_PERMISSION_DENIED;
    }
    // 2. 参数验证
    if (attrs.type < FIRST_APPLICATION_WINDOW ||
        attrs.type > LAST_APPLICATION_WINDOW) {
        // 系统窗口需要特殊权限
    }
    // 3. 获取或创建WindowToken
    WindowToken token = getWindowToken(attrs.token);
    if (token == null) {
        // 创建新Token
        token = new WindowToken(...);
    }
    // 4. 创建WindowState
    WindowState win = new WindowState(this, session, client, token, ...);
    // 5. 添加到DisplayContent
    DisplayContent displayContent = getDisplayContent(displayId);
    displayContent.addWindow(win);
    // 6. 创建Surface
    win.createSurfaceControl();
    // 7. 更新布局
    performLayoutAndPlaceSurfacesLocked();
    return WindowManagerGlobal.ADD_OKAY;
}
```
**关键步骤:**
1. **权限检查**:检查是否有权限添加该类型窗口
2. **Token管理**获取或创建WindowToken
3. **创建WindowState**:创建窗口状态对象
4. **添加到DisplayContent**:将窗口添加到对应的显示设备
5. **创建Surface**创建窗口的Surface用于绘制
6. **更新布局**:重新计算所有窗口的布局
### Q11: performLayoutAndPlaceSurfacesLocked方法的作用是什么
**答案:**
**方法作用:**
`performLayoutAndPlaceSurfacesLocked`是WMS的核心布局方法负责
1. 计算所有窗口的位置和大小
2. 分配窗口的层级
3. 更新Surface的位置和大小
4. 处理窗口动画
**执行流程:**
```java
void performLayoutAndPlaceSurfacesLocked() {
    // 1. 遍历所有显示设备
    for (DisplayContent displayContent : mDisplayContents) {
        // 2. 计算显示配置
        displayContent.computeScreenConfiguration();
        // 3. 应用窗口策略
        applyWindowPolicy(displayContent);
        // 4. 计算窗口位置和大小
        for (WindowState win : displayContent.mWindows) {
            computeWindowFrame(win);
        }
        // 5. 分配窗口层级
        assignWindowLayers(displayContent);
        // 6. 更新Surface
        for (WindowState win : displayContent.mWindows) {
            if (win.mHasSurface) {
                updateSurface(win);
            }
        }
        // 7. 处理窗口动画
        displayContent.mAnimator.updateWindowsLocked();
    }
}
```
### Q12: WMS如何与SurfaceFlinger交互
**答案:**
**交互流程:**
```
WMS
  ├──> 创建SurfaceControl
  ├──> 设置Surface属性位置、大小、层级
  └──> 通过SurfaceControl与SurfaceFlinger通信
       └──> SurfaceFlinger合成Surface并显示
```
**关键代码:**
```java
// WMS创建Surface
class WindowState {
    void createSurfaceControl() {
        mSurfaceControl = new SurfaceControl.Builder()
            .setName(mWindowTag)
            .setBufferSize(mFrame.width(), mFrame.height())
            .setFormat(PixelFormat.TRANSLUCENT)
            .build();
        mSurface = new Surface(mSurfaceControl);
    }
    void updateSurface() {
        // 设置Surface位置和大小
        mSurfaceControl.setPosition(mFrame.left, mFrame.top);
        mSurfaceControl.setSize(mFrame.width(), mFrame.height());
        // 设置Surface层级
        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
        t.setLayer(mSurfaceControl, mLayer);
        t.apply();
    }
}
```
---
## 性能优化类
### Q13: 如何优化窗口的创建和显示性能?
**答案:**
**优化策略:**
1. **减少窗口数量**:避免创建过多窗口
2. **延迟创建**:非关键窗口延迟创建
3. **复用窗口**:复用已有的窗口
4. **优化布局**:减少布局层级和复杂度
5. **使用硬件加速**:启用硬件加速提升绘制性能
```java
// 1. 延迟创建窗口
Handler.postDelayed(() -> {
    createWindow();
}, 100);
// 2. 复用窗口
if (mWindow == null) {
    mWindow = createWindow();
} else {
    updateWindow(mWindow);
}
// 3. 启用硬件加速
<activity android:hardwareAccelerated="true" />
```
### Q14: 窗口动画对性能有什么影响?如何优化?
**答案:**
**影响:**
1. **CPU占用**动画计算消耗CPU
2. **GPU占用**动画渲染消耗GPU
3. **帧率下降**:复杂动画可能导致掉帧
**优化方法:**
1. **简化动画**:使用简单的动画效果
2. **使用硬件加速**利用GPU加速动画
3. **减少动画时长**:缩短动画时间
4. **避免同时多个动画**:减少并发动画数量
```java
// 使用硬件加速动画
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
animator.setDuration(300);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
```
---
## 问题排查类
### Q15: 如何排查窗口显示异常的问题?
**答案:**
**排查步骤:**
1. **检查窗口状态**:使用`adb shell dumpsys window`
2. **检查Surface状态**查看Surface是否创建成功
3. **检查布局**查看窗口的Frame是否正确
4. **检查层级**查看窗口的Layer是否正确
5. **使用Systrace**:分析窗口创建和布局流程
```bash
# 1. 查看窗口信息
adb shell dumpsys window windows
# 2. 查看窗口层级
adb shell dumpsys window displays
# 3. 使用Systrace分析
python systrace.py -t 10 -o trace.html wm
```
### Q16: 窗口泄漏如何排查和解决?
**答案:**
**排查方法:**
1. **检查WindowManager引用**:确保及时移除窗口
2. **检查生命周期**确保在onDestroy中移除窗口
3. **使用LeakCanary**:检测窗口泄漏
4. **检查WindowToken**确保Token正确管理
```java
// 正确的窗口管理
class MyActivity extends Activity {
    private View floatingView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 创建悬浮窗
        createFloatingWindow();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 必须移除窗口
        if (floatingView != null) {
            WindowManager wm = getWindowManager();
            wm.removeView(floatingView);
            floatingView = null;
        }
    }
}
```
---
## 多窗口相关
### Q17: 分屏模式是如何实现的?
**答案:**
**分屏实现原理:**
1. **任务栈管理**每个分屏窗口对应一个TaskStack
2. **窗口布局**WMS计算每个窗口的位置和大小
3. **输入事件分发**:根据触摸位置分发到对应窗口
4. **焦点管理**:管理两个窗口的焦点切换
```java
// 分屏窗口布局
void layoutSplitScreenWindows(DisplayContent displayContent) {
    // 计算分屏窗口大小
    Rect primaryFrame = new Rect(0, 0, screenWidth / 2, screenHeight);
    Rect secondaryFrame = new Rect(screenWidth / 2, 0, screenWidth, screenHeight);
    // 设置主窗口位置
    primaryWindow.mFrame = primaryFrame;
    // 设置次窗口位置
    secondaryWindow.mFrame = secondaryFrame;
}
```
### Q18: 悬浮窗Overlay Window如何实现
**答案:**
**实现步骤:**
1. **申请权限**`SYSTEM_ALERT_WINDOW`权限
2. **设置窗口类型**`TYPE_APPLICATION_OVERLAY`
3. **创建窗口**使用WindowManager添加窗口
4. **管理生命周期**:及时移除窗口
```java
// 创建悬浮窗
private void createOverlayWindow() {
    // 1. 检查权限
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            // 请求权限
            requestOverlayPermission();
            return;
        }
    }
    // 2. 创建窗口参数
    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT
    );
    // 3. 创建视图
    View overlayView = LayoutInflater.from(this)
        .inflate(R.layout.overlay_window, null);
    // 4. 添加窗口
    WindowManager wm = getSystemService(Context.WINDOW_SERVICE);
    wm.addView(overlayView, params);
}
```
---
## 总结
WMS窗口面试题主要涵盖
1. **基础概念**WMS作用、窗口类型、层级关系
2. **架构原理**WMS架构、窗口添加流程、Z-Order管理
3. **窗口管理**WindowState、D