锁屏快捷方式上划 离屏渲染(drawLayer) 优化总结

一、问题现象

快捷方式上划过程中,Systrace 出现 drawLayer [FrameLayout] 1224.0 x 2912.0,表明 HWUI 为一个全屏尺寸的 FrameLayout 创建了离屏缓冲区(offscreen buffer),造成额外的 GPU 开销。

二、离屏渲染原理

触发条件

当一个 View 同时满足以下两个条件时,HWUI 会为其创建离屏缓冲区:

  1. View.alpha 处于 0~1 之间(不含 0 和 1)
  2. hasOverlappingRendering() 返回 true(FrameLayout 默认返回 true)

为什么有性能问题

  • HWUI 先将该 View 及所有子 View 绘制到一个等大的离屏纹理中
  • 然后对整个纹理应用 alpha 后再绘制到屏幕上
  • 对于全屏尺寸(1224x2912)的 View,这意味着额外分配和渲染一张全屏 GPU 纹理,开销很大

解决方式

重写 hasOverlappingRendering() 返回 false,告诉 HWUI 跳过离屏缓冲区,直接对每个子 View 的绘制操作逐一应用 alpha。对于子 View 不存在重叠渲染的场景,视觉效果完全一致,但性能显著提升。

三、定位到的两个离屏渲染源

问题1:ShortcutWindowManager.rootView(MiuiAod 项目)

  • 文件keyguardshortcuts/src/main/java/com/miui/keyguard/shortcuts/manager/ShortcutWindowManager.kt
  • 原因:rootView 是一个普通 FrameLayout,ShortcutWindowManager.setAlpha() 在动画过程中设置 rootView?.alpha,触发离屏渲染
  • 修复:rootView 创建时重写 hasOverlappingRendering() = false
// 修改前
rootView = FrameLayout(context)
 
// 修改后
rootView = object : FrameLayout(context) {
    override fun hasOverlappingRendering() = false
}

问题2:miui_keyguard_foreground_clock_container(SystemUI 项目)

  • 文件/home/zbc/micode/MiuiSystemUI/packages/SystemUI/miui/Keyguard/res/layout/keyguard_panel_view.xml
  • 原因:该容器在 XML 中声明为普通 FrameLayout(match_parent x match_parent),在快捷方式上划时 KeyguardPanelViewInjector.onHorizontalMove() 通过 MobileView.setAlpha(1 - per) 设置其 alpha 值,触发全屏离屏渲染
  • Alpha 设置链路
    KeyguardPanelViewInjector.onHorizontalMove()
      → providerMobileKeyguardViews().forEach { mobileView.setAlpha(1 - per) }
        → MobileView.setAlpha() → view.setAlpha()
          → 触发 miui_keyguard_foreground_clock_container 的离屏渲染
    
  • 修复:将 XML 中的 FrameLayout 替换为项目已有的 AlphaOptimizedFrameLayout
<!-- 修改前 -->
<FrameLayout
    android:id="@+id/miui_keyguard_foreground_clock_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
 
<!-- 修改后 -->
<com.miui.systemui.widget.AlphaOptimizedFrameLayout
    android:id="@+id/miui_keyguard_foreground_clock_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  • 兼容性AlphaOptimizedFrameLayout 继承自 FrameLayout,所有通过 FrameLayout? 类型引用该 View 的代码(KeyguardClockInjectorKeyguardClockContainerKeyguardPanelViewController)均无需修改

AlphaOptimizedFrameLayout 源码

位于 packages/SystemUI/miuiModules/Base/src/com/miui/systemui/widget/AlphaOptimizedFrameLayout.kt

open class AlphaOptimizedFrameLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    @AttrRes defStyleAttr: Int = 0,
    @StyleRes defStyleRes: Int = 0
) : android.widget.FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
    override fun hasOverlappingRendering() = false
}

四、同期优化:背景模糊注释

  • 文件keyguardshortcuts/src/main/java/com/miui/keyguard/shortcuts/controller/BaseShortcutAnimController.kt
  • 修改applyBlurRatio() 方法体中的 blurHelper.applyBlur() 调用已注释,禁用了快捷方式动画过程中的窗口背景模糊效果
  • 说明ViewBlurHelper(HWUI Mi Background Blur)经确认为死代码,未被任何地方调用

五、编译验证

MiuiAod 项目(问题1 + 背景模糊)

cd /home/zbc/pangu/MiuiAod
./gradlew :keyguardshortcuts:assembleWithAodDebug
# APK 产出:keyguardshortcuts/build/outputs/apk/withAod/debug/
adb -s fb181e67 push <apk> /data/MiuiAodApk/MiuiAod.apk

SystemUI 项目(问题2)

cd /home/zbc/micode/MiuiSystemUI
./gradlew :SystemUI:assembleOverlayMiuiCnDefaultPhoneDebug
# APK 产出:packages/SystemUI/build/outputs/apk/overlayMiuiCnDefaultPhone/debug/
adb -s fb181e67 push <apk> /system_ext/priv-app/MiuiSystemUI/MiuiSystemUI.apk
adb -s fb181e67 shell kill $(adb -s fb181e67 shell pidof com.android.systemui)

六、验证方法

  1. Systrace 验证:抓取快捷方式上划过程的 trace,确认不再出现 drawLayer [FrameLayout] 1224.0 x 2912.0
  2. 布局检查:通过 Layout Inspector 确认 miui_keyguard_foreground_clock_container 的类型已变为 AlphaOptimizedFrameLayout
  3. 功能验证:快捷方式上划动画、锁屏时钟显示、杂志锁屏等功能正常