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 释放资源,比如取消动画、清空回调
关键点:KeyguardHostView 的 onAttachedToWindow 可能会被多次调用——每次锁屏重建时都会重走一遍。我建议在这里只做轻量级的初始化,复杂逻辑放到 showPrimarySecurityScreen 里。

另外,KHV 内部还有一个 View Mediator 的概念,用来协调不同安全视图之间的切换动画。比如从 PIN 切换到指纹识别,中间不能有黑色闪屏。我记得有一次单测里发现切换时出现了白屏,排查下来是因为动画的 View 在 dispatchDraw 阶段没有同步背景颜色。

3.3 两者如何协作

KVM 和 KHV 的关系,可以用下面这张图来理解:

锁屏核心组件关系图 系统事件 KeyguardViewMediator 状态管理 · 策略决策 onScreenTurnedOn/Off show/hide → Handler KeyguardHostView UI展示 · 用户交互 showPrimarySecurityScreen ViewGroup 生命周期 PIN/图案/密码视图 内部状态机 显示中 / 隐藏中 / 空闲

流程很清楚:系统事件(亮屏、灭屏、指纹成功等)同时发给 KVM 和 KHV,但 KVM 先做决策,然后通过 Handler 通知 KHV 显示或隐藏。KHV 拿到指令后,才会切换到对应的安全视图。

避坑提醒:我曾经遇到锁屏闪烁问题,原因是 KVM 的状态更新与 KHV 的绘制不同步。比如 KVM 已经标记为“隐藏中”,但 KHV 的动画还在进行,结果下一次亮屏时,旧动画和新视图冲突了。我的修复方法是:在 KHV 的 handleShow 里加一个 synchronized 块,确保状态切换是原子的。

3.4 实战中的常见问题

  1. 锁屏延迟显示:通常是 KVM 的 onScreenTurnedOn 里做了太重的事情,比如读取加密存储、加载配置。我建议把这类操作 post 到下一个 Looper 周期。
  2. 解锁后残留视图:KHV 的 cleanUp 没做好,导致某些 View 仍然持有 WindowManager 的引用。解决办法:在 cleanUp 里遍历子 View 并 remove。
  3. 多次重建:锁屏在某些低内存设备上会被 SystemUI 杀死重建。KVM 和 KHV 都会重新初始化,但它们的回调时序容易错乱。我习惯在重建后加一个 reloadState() 方法,从 SharedPreferences 恢复之前的锁屏状态。

3.5 总结一下核心要点

组件职责关键接口常见坑
KeyguardViewMediator状态机 + 决策show/hide/onScreenTurnedOn主线程耗时
KeyguardHostViewUI容器 + 交互showPrimarySecurityScreen/cleanUp重建后状态丢失

我个人习惯把 KVM 和 KHV 视为一个“锁屏引擎”的两个面。调试的时候,用 adb shell dumpsys activity service com.android.systemui/.SystemUIService 可以抓到锁屏栈,里面包含了两个组件的当前状态。遇到奇怪问题时先看这个 dump,往往能直接定位。

嗯,今天就先聊这么多。这两个类掌握了,剩下的安全视图、通知面板之类的基本就是“子民”了。