WindowManager对窗口的操作

1,183 阅读6分钟

Android Window体系与绘制流程(含核心图示与源码)


1. Window、WindowManager、WindowManagerService 关系

Window:Android所有界面内容的承载体,View必须挂载在Window上才能显示。
WindowManager:管理Window的API,包括添加、更新、删除等。
WindowManagerService (WMS) :系统服务,管理所有窗口的层级、生命周期、动画等。

Window/WindowManager/WindowManagerService关系


2. Window 分类与层级

  • Application Window:Activity界面
  • Sub Window:Dialog、PopupWindow等,依附主窗口
  • System Window:输入法、音量条、系统错误等

Window类型

TYPE_APPLICATION_STARTING(启动页窗口):

public static final int TYPE_APPLICATION_STARTING = 3;

窗口层级:
窗口层级图


3. Window 参数与标志

FLAG_NOT_FOCUSABLE = 0x00000008; // 无焦点
FLAG_NOT_TOUCH_MODAL  = 0x00000020; // 只处理自己区域的触摸
FLAG_NOT_TOUCHABLE; // 不接收触摸
FLAG_KEEP_SCREEN_ON; // 屏幕常亮
FLAG_LAYOUT_NO_LIMITS; // 可超屏
FLAG_SHOW_WHEN_LOCKED = 0x00080000; // 锁屏显示

4. 核心类之间关系

  • Activity → PhoneWindow → DecorView
    (DecorView是FrameLayout子类,为View树根节点)

Activity-PhoneWindow-DecorView关系

attach-DecorView流程


5. 添加Window和ViewRootImpl结构

5.1 添加流程源码

handleResumeActivity
   --->performResumeActivity
      --->r.activity.performResume
         --->mInstrumentation.callActivityOnResume(this);
   --->wm.addView(decor, l); // 重点
      --->WindowManagerGlobal.addView
         --->root = new ViewRootImpl(view.getContext(), display);
         --->mViews.add(view) // DecorView
         --->mRoots.add(root);// ViewRootImpl
         --->mParams.add(wparams);// WindowlayouParams
         --->root.setView(view, wparams, panelParentView, userId);

Window添加流程

  • WindowManagerImpl:管理PhoneWindow
  • WindowManagerGlobal:管理全局所有窗口
  • ViewRootImpl:每个窗口(DecorView)独有,负责View树的刷新、输入等

6. ViewRootImpl作用与构造

  • View树的根,管理measure/layout/draw/输入分发/与WMS通信
  • View最终的parent一定是ViewRootImpl
ViewRootImpl {
    mThread = Thread.currentThread();// 默认MainThread
    mDirty = new Rect(); // 脏区
    mAttachInfo = new View.AttachInfo(mWindowSession, ...); // WMS通信Binder
}

ViewRootImpl结构


7. 绘制主流程

7.1 performTraversals

performTraversals()
   --->windowSizeMayChange |= measureHierarchy(host) // 预测量
   --->relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); // 布局窗口
   --->performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 测量
   --->performLayout(lp, mWidth, mHeight); // 布局
   --->performDraw();// 绘制

绘制流程图

measureHierarchy预测量流程(最多三次):

measureHierarchy()  onMeasure最多会执行多少次?
---->第一次测量
---->goodMeasure状态
---->baseSize = (baseSize+desiredWindowWidth)/2; 再测量
---->不满意再测量(最大值)

7.2 performMeasure、onMeasure

performMeasure
   --->View.measure()
      --->onMeasure() // setMeasuredDimension 必须调用,否则抛异常

MeasureSpec格式说明:
高2位模式,低30位size(EXACTLY/UNSPECIFIED/AT_MOST)


8. 子线程UI、刷新与局部重绘

  • requestLayout() /invalidate()等最终都要回到创建ViewRootImpl的线程才可生效

    • 子线程可以创建自己的ViewRootImpl来做窗口级别的UI
  • 局部刷新只会重绘脏区,不会全量重新measure/layout/draw


9. WindowManagerGlobal/窗口更新与刷新

WindowManagerGlobal.updateViewLayout()

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    // ...略
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    view.setLayoutParams(wparams);

    synchronized (mLock) {
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots.get(index);
        mParams.remove(index);
        mParams.add(index, wparams);
        root.setLayoutParams(wparams, false);
    }
}

触发ViewRootImpl.scheduleTraversals()


10. 刷新流程与同步栅栏机制

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 发出同步栅栏
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // 注册vsync,等待下一帧
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

刷新流程示意


11. SurfaceFlinger渲染原理

  • SurfaceFlinger (SF)
    系统服务,所有App和系统窗口的最终渲染/合成者

SF架构

生产者-消费者架构

  • 应用进程产生Canvas(Surface),SF合成并交给显示。

12. 事件分发

事件分发链路


13. 面试常问问题解答

  • 连续两次setTextView会重绘几次?为什么60FPS?

    • 合并为一次重绘。60FPS由VSYNC每16.6ms驱动,只有有脏区才会刷新。
  • Activity/Window/View区别?

    • Activity负责生命周期,Window承载View,View为最终显示单元。
  • onResume时能否拿到宽高?

    • 第一次onResume时不行,只有在Window真正attach、DecorView添加后才能measure。
    • handleResumeActivity→makeVisible→addView→setView→requestLayout后View树measure。
  • ViewGroup为何不执行onDraw?

    • 因为ViewGroup默认有PFLAG_SKIP_DRAW,只有dispatchDraw而不会走onDraw。

学后检测

一、单选题

1. DecorView是在Activity生命周期的哪个阶段被添加到Window上的?
A. onCreate之前
B. onStart之后
C. onResume之后
D. onPause之前

答案:C
解析:

  • DecorView是在Activity的onResume流程结束后,通过Activity.makeVisible()调用,最终走到WindowManager.addView()才被真正添加到Window上的。
  • 相关源码:handleResumeActivitymakeVisible()

2. 下列关于WindowManagerGlobal的说法,正确的是?
A. 每个Activity各自维护一份
B. 每个Window对应一个WindowManagerGlobal
C. 进程唯一,负责管理所有窗口的View、Root等
D. 只有系统窗口才用到WindowManagerGlobal

答案:C
解析:

  • WindowManagerGlobal是进程级的单例,管理当前进程所有View、ViewRootImpl、LayoutParams等。Activity、Dialog等都共用同一个WindowManagerGlobal实例。

3. 下列哪个flag会导致Window无法获得输入焦点?
A. FLAG_NOT_TOUCH_MODAL
B. FLAG_KEEP_SCREEN_ON
C. FLAG_NOT_FOCUSABLE
D. FLAG_SHOW_WHEN_LOCKED

答案:C
解析:

  • FLAG_NOT_FOCUSABLE使Window不能获得输入焦点(常用于悬浮窗等)。
  • 其他选项控制的是Touch/亮屏/显示于锁屏等。

二、多选题

4. 关于ViewRootImpl,下列哪些说法是正确的?
A. 一个Window通常有一个ViewRootImpl对应
B. 负责触发View树的measure/layout/draw
C. 负责与WMS跨进程通信
D. 可以在子线程创建,但生产环境绝大多数在主线程

答案:A、B、C、D
解析:

  • 四项都正确。少数场景下(如自定义线程+Looper.prepare())可在子线程创建ViewRootImpl并独立刷新。

5. 下列哪些属于System Window类型?
A. Activity主窗口
B. 输入法窗口
C. Dialog窗口
D. 系统音量条窗口

答案:B、D
解析:

  • B和D是系统窗口(如TYPE_INPUT_METHOD、TYPE_SYSTEM_ALERT等)。
  • Activity主窗口、Dialog属于Application Window或Sub Window。

三、判断题

6. ViewGroup默认不会回调onDraw()方法。
答案:正确
解析:

  • ViewGroup的onDraw默认不会被调用,除非手动清除PFLAG_SKIP_DRAW。

7. 连续两次setText()会导致两次UI重绘。
答案:错误
解析:

  • Android UI刷新基于VSYNC信号,每帧只处理一次刷新,多次invalidate合并到下一帧。

8. SurfaceFlinger负责所有应用的最终图像合成和显示。
答案:正确
解析:

  • SurfaceFlinger是Android系统服务,合成各应用Surface,统一输出到屏幕。

9. 可以在Activity的onResume()方法内获取根View(DecorView)的准确宽高。
答案:错误
解析:

  • DecorView是在onResume结束后才被addView,measure/layout流程尚未走完,需在onGlobalLayout回调中获取。

四、简答题

10. 简述Window、WindowManager和WMS的关系和区别。
答案与解析:

  • Window:抽象的窗口对象(如PhoneWindow),是View显示的载体。
  • WindowManager:负责管理Window的添加、更新和删除,是应用与系统服务的桥梁。
  • WMS(WindowManagerService) :系统进程服务,真正管理窗口的层级、显示、输入等。
  • 三者协作:APP通过WindowManager请求操作→WindowManager转发到WMS→WMS统一管理窗口→ViewRootImpl负责View的绘制和刷新。

11. 为什么连续两次setText只会触发一次UI重绘?Android系统如何保证刷新节奏?
答案与解析:

  • Android UI刷新基于VSYNC机制,每16.6ms最多绘制一帧,多次setText/invalidate只设置标志位,等下一帧VSYNC到来时批量处理。
  • scheduleTraversals→performTraversals只会被调度一次,避免冗余重绘,提高性能。

12. ViewRootImpl的作用是什么?
答案与解析:

  • ViewRootImpl是连接View层和Window系统的桥梁,负责View树的measure、layout、draw,UI刷新、输入事件派发、窗口添加/更新、与WMS通信等核心任务,是View绘制流程的真正调度者。

五、源码填空题

13. 整个View树的measure、layout和draw流程统一由_______方法调度。
答案:performTraversals()
解析:

  • ViewRootImpl.performTraversals()负责整个刷新流程。

14. WindowManagerGlobal.updateViewLayout()会找到DecorView对应的_______对象,然后触发setLayoutParams参数更新和刷新。
答案:ViewRootImpl
解析:

  • updateViewLayout会找到DecorView的ViewRootImpl,更新LayoutParams并scheduleTraversals。