28、WindowManager 的性能优化:减少 addView 次数、合理使用 Flag

各位同学,今天我们来聊聊 WindowManager 的性能优化。说实话,这个主题我特别想讲,因为我在项目中踩过不少坑。你想想看,一个 App 卡顿、掉帧,很多时候不是业务逻辑的问题,而是窗口管理没做好。

WindowManager 的性能优化,说白了就两件事:减少 addView 的次数,以及合理使用 Flag。这两点做好了,你的界面流畅度能提升一个档次。

核心观点:每一次 addView 都是一次系统级的资源分配和布局计算。能复用就复用,能合并就合并。

28.1 减少 addView 次数:能复用就别新建

我个人习惯是,在需要动态添加窗口时,先问问自己:这个窗口能不能提前创建好?能不能复用?

举个例子,我之前做一个悬浮球功能,用户点击悬浮球弹出菜单。一开始我每次点击都 new 一个 View,然后 addView。结果呢?快速点击几次,界面就开始卡顿了。

后来我改成:在初始化时就把所有 View 创建好,通过 setVisibility 来控制显示隐藏。效果立竿见影。

// ❌ 错误做法:每次点击都 addView
public void showMenu() {
    View menuView = LayoutInflater.from(context).inflate(R.layout.menu, null);
    windowManager.addView(menuView, params);
}

// ✅ 正确做法:复用 View,只控制可见性
private View menuView;

public void init() {
    menuView = LayoutInflater.from(context).inflate(R.layout.menu, null);
    windowManager.addView(menuView, params);
    menuView.setVisibility(View.GONE);
}

public void showMenu() {
    if (menuView != null) {
        menuView.setVisibility(View.VISIBLE);
    }
}

小技巧:如果你确实需要频繁 addView/removeView,可以考虑使用 ViewStub 或者对象池模式。我在做直播弹幕时就用过对象池,效果不错。

28.2 合理使用 Flag:别让系统做无用功

WindowManager 的 Flag 很多,但常用的就那么几个。我见过不少开发者,不管三七二十一,上来就加一堆 Flag,结果性能反而变差了。

为什么会这样?因为每个 Flag 都对应着系统的一些额外处理逻辑。比如 FLAG_NOT_FOCUSABLEFLAG_NOT_TOUCHABLE,如果你不需要触摸事件,就别加 FLAG_NOT_TOUCH_MODAL,后者会让系统做更多的事件分发判断。

Flag 作用 性能影响 建议
FLAG_NOT_FOCUSABLE 窗口不获取焦点 常用,推荐
FLAG_NOT_TOUCHABLE 窗口不响应触摸 按需使用
FLAG_NOT_TOUCH_MODAL 窗口外的触摸事件透传 谨慎使用
FLAG_LAYOUT_IN_SCREEN 窗口铺满屏幕 避免滥用
FLAG_HARDWARE_ACCELERATED 开启硬件加速 高(正面) 推荐开启

注意:我曾经在一个项目中,为了做一个悬浮窗,同时加了 FLAG_NOT_FOCUSABLE、FLAG_NOT_TOUCHABLE、FLAG_NOT_TOUCH_MODAL 三个 Flag。结果触摸事件处理得一塌糊涂,排查了半天才发现是 Flag 冲突了。

28.3 实战中的避坑指南

嗯,这里我总结几个常见的坑,都是我在项目中真实遇到过的。

  • 不要在主线程频繁 addView:addView 会触发 measure/layout/draw,如果主线程正在做其他耗时操作,很容易掉帧。建议在子线程准备好 View,再切回主线程 addView。
  • 注意 WindowManager 的更新频率:如果你需要频繁更新窗口位置(比如拖拽悬浮球),使用 updateViewLayout 而不是 removeView 再 addView。后者会重新走一遍完整的窗口创建流程。
  • 合理使用 Type:TYPE_APPLICATION_OVERLAY 是 Android 8.0 之后推荐的悬浮窗类型,性能比 TYPE_PHONE 好。我建议新项目都使用这个类型。
// 推荐的做法:使用 TYPE_APPLICATION_OVERLAY
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
        WindowManager.LayoutParams.TYPE_PHONE,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT
);

28.4 知识体系总览

下面这张图是我自己整理的 WindowManager 性能优化核心逻辑,你可以对照着看。

WindowManager 性能优化核心逻辑 减少 addView 次数 合理使用 Flag 复用 View 对象池模式 预创建 View updateViewLayout 避免冲突 Flag 按需添加 硬件加速 TYPE 选择 目标:减少系统资源消耗,提升界面流畅度

28.5 总结

WindowManager 的性能优化,其实没有太多高深的东西。核心就是:少创建、多复用、选对 Flag。我在做性能优化时,经常用 Profiler 工具观察 addView 的调用频率,一旦发现某个窗口被频繁创建销毁,就说明有优化空间。

好了,这一章的内容就到这里。记住,优化不是一蹴而就的,而是在日常开发中一点一滴积累的。希望你能把这些技巧用到实际项目中。


公众号:蓝海资料掘金营,微信deep3321