3. 锁屏核心组件解读:KeyguardViewMediator、KeyguardHostView
锁屏系统里最让我头疼的两个类,就是 KeyguardViewMediator 和 KeyguardHostView。一个管决策,一个管展示。搞懂了它们,锁屏优化的半壁江山就稳了。
3.1 KeyguardViewMediator —— 决策中心
KeyguardViewMediator 说白了就是个“大管家”。它监听系统事件(亮屏、灭屏、锁屏意图、指纹解锁等等),然后决定要不要显示锁屏、显示哪个安全视图。我习惯叫它 KVM。
KVM 内部维护着好几套状态:
- 显示状态:显示中、隐藏中、正在淡入淡出
- 用户交互状态:用户是否正在输入、是否已解锁
- 安全状态:是否需要 PIN/图案/密码验证
| 核心方法 | 作用 |
|---|---|
onScreenTurnedOn(int why) | 亮屏时调用,触发锁屏显示逻辑 |
onScreenTurnedOff(int why) | 灭屏时调用,准备隐藏锁屏或重置状态 |
show(Bundle options) | 主动显示锁屏,通常来自 SystemUI 的启动 |
hide() | 隐藏锁屏(用户已解锁或屏幕关闭) |
handleShow() | 异步执行显示流程,包括通知 KeyguardHostView |
嗯,这里要注意:KVM 的很多方法都是在 主线程 Handler 里回调的。我曾经踩过一个坑——在 onScreenTurnedOn 里直接做了耗时操作,结果锁屏弹出来卡了半秒。解决方案很简单:用 Handler.post 或者丢到子线程。你想想看,亮屏那瞬间用户眼睛还没睁开呢,如果卡顿,体验极差。
3.2 KeyguardHostView —— 展示引擎
KeyguardHostView 是一个 ViewGroup,负责装载所有的锁屏 UI:解锁界面、通知栏、快捷开关、紧急呼叫按钮……它本身不决策,只忠实地执行 KVM 下达的“显示”或“隐藏”指令。
它的生命周期和 Activity 很像:
onAttachedToWindow时绑定 Window 和输入法showPrimarySecurityScreen切换主安全视图(PIN/图案/密码)cleanUp释放资源,比如取消动画、清空回调
onAttachedToWindow 可能会被多次调用——每次锁屏重建时都会重走一遍。我建议在这里只做轻量级的初始化,复杂逻辑放到 showPrimarySecurityScreen 里。
另外,KHV 内部还有一个 View Mediator 的概念,用来协调不同安全视图之间的切换动画。比如从 PIN 切换到指纹识别,中间不能有黑色闪屏。我记得有一次单测里发现切换时出现了白屏,排查下来是因为动画的 View 在 dispatchDraw 阶段没有同步背景颜色。
3.3 两者如何协作
KVM 和 KHV 的关系,可以用下面这张图来理解:
流程很清楚:系统事件(亮屏、灭屏、指纹成功等)同时发给 KVM 和 KHV,但 KVM 先做决策,然后通过 Handler 通知 KHV 显示或隐藏。KHV 拿到指令后,才会切换到对应的安全视图。
handleShow 里加一个 synchronized 块,确保状态切换是原子的。
3.4 实战中的常见问题
- 锁屏延迟显示:通常是 KVM 的
onScreenTurnedOn里做了太重的事情,比如读取加密存储、加载配置。我建议把这类操作post到下一个 Looper 周期。 - 解锁后残留视图:KHV 的
cleanUp没做好,导致某些 View 仍然持有 WindowManager 的引用。解决办法:在cleanUp里遍历子 View 并 remove。 - 多次重建:锁屏在某些低内存设备上会被 SystemUI 杀死重建。KVM 和 KHV 都会重新初始化,但它们的回调时序容易错乱。我习惯在重建后加一个
reloadState()方法,从 SharedPreferences 恢复之前的锁屏状态。
3.5 总结一下核心要点
| 组件 | 职责 | 关键接口 | 常见坑 |
|---|---|---|---|
| KeyguardViewMediator | 状态机 + 决策 | show/hide/onScreenTurnedOn | 主线程耗时 |
| KeyguardHostView | UI容器 + 交互 | showPrimarySecurityScreen/cleanUp | 重建后状态丢失 |
我个人习惯把 KVM 和 KHV 视为一个“锁屏引擎”的两个面。调试的时候,用 adb shell dumpsys activity service com.android.systemui/.SystemUIService 可以抓到锁屏栈,里面包含了两个组件的当前状态。遇到奇怪问题时先看这个 dump,往往能直接定位。
嗯,今天就先聊这么多。这两个类掌握了,剩下的安全视图、通知面板之类的基本就是“子民”了。