锁屏快捷方式上划 离屏渲染(drawLayer) 优化总结
一、问题现象
快捷方式上划过程中,Systrace 出现 drawLayer [FrameLayout] 1224.0 x 2912.0,表明 HWUI 为一个全屏尺寸的 FrameLayout 创建了离屏缓冲区(offscreen buffer),造成额外的 GPU 开销。
二、离屏渲染原理
触发条件
当一个 View 同时满足以下两个条件时,HWUI 会为其创建离屏缓冲区:
- View.alpha 处于 0~1 之间(不含 0 和 1)
- 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 的代码(KeyguardClockInjector、KeyguardClockContainer、KeyguardPanelViewController)均无需修改
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.apkSystemUI 项目(问题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)六、验证方法
- Systrace 验证:抓取快捷方式上划过程的 trace,确认不再出现
drawLayer [FrameLayout] 1224.0 x 2912.0 - 布局检查:通过 Layout Inspector 确认
miui_keyguard_foreground_clock_container的类型已变为AlphaOptimizedFrameLayout - 功能验证:快捷方式上划动画、锁屏时钟显示、杂志锁屏等功能正常