Android Window体系与绘制流程(含核心图示与源码)
1. Window、WindowManager、WindowManagerService 关系
Window:Android所有界面内容的承载体,View必须挂载在Window上才能显示。
WindowManager:管理Window的API,包括添加、更新、删除等。
WindowManagerService (WMS) :系统服务,管理所有窗口的层级、生命周期、动画等。
2. Window 分类与层级
- Application Window:Activity界面
- Sub Window:Dialog、PopupWindow等,依附主窗口
- System 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树根节点)
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);
- 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
}
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和系统窗口的最终渲染/合成者
- 应用进程产生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上的。 - 相关源码:
handleResumeActivity和makeVisible()。
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。