4、FlutterEngine的创建与管理:创建单例FlutterEngine、缓存策略、生命周期管理、内存优化

FlutterEngine 这东西,说白了就是 Flutter 在原生世界里的「发动机」。你把它启动起来,Dart 代码才能跑,UI 才能渲染。但很多新手容易犯一个错——每次打开 Flutter 页面都 new 一个 Engine。我刚开始也这么干过,结果内存直接飙到 200MB+,用户反馈说 App 越用越卡。嗯,今天我们就来聊聊怎么管好这个 Engine。

4.1 为什么需要单例 FlutterEngine?

你想想看,一个 FlutterEngine 创建出来,背后要做多少事:

  • 加载 Dart VM(虚拟机)
  • 初始化 Flutter 框架
  • 加载 Dart 业务代码(AOT 或 JIT)
  • 创建渲染管线、平台通道

这些操作加起来,少说也要几百毫秒。如果你每次打开页面都重新创建,用户就会看到白屏、卡顿。更糟糕的是,多个 Engine 同时存在,内存直接翻倍。

核心原则: 一个 App 进程内,尽量只维护一个 FlutterEngine 实例。除非你有特殊需求(比如多 isolate 场景),否则别搞多个。

4.2 创建单例 FlutterEngine

我个人习惯在 Application 的 onCreate 里提前创建好 Engine。这样用户打开第一个 Flutter 页面时,Engine 已经预热完毕,几乎秒开。

// 在 Application 中初始化
class MyApplication : Application() {
    lateinit var flutterEngine: FlutterEngine

    override fun onCreate() {
        super.onCreate()
        // 创建单例 Engine
        flutterEngine = FlutterEngine(this)
        // 启动 Dart 执行器
        flutterEngine.dartExecutor.executeDartEntrypoint(
            DartExecutor.DartEntrypoint.createDefault()
        )
        // 注册到缓存管理
        FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine)
    }
}

这里有个细节:executeDartEntrypoint 是异步的,不会阻塞主线程。所以放在 Application 里完全没问题。

小技巧: 如果你有多个 Flutter 入口(比如首页和详情页都用 Flutter),可以只创建一个 Engine,通过路由来区分页面。这样内存只占一份。

4.3 缓存策略:FlutterEngineCache

Flutter 官方提供了一个 FlutterEngineCache 类,专门用来缓存 Engine。它的用法很简单:

// 存入缓存
FlutterEngineCache.getInstance().put("engine_id", engine)

// 从缓存取出
val cachedEngine = FlutterEngineCache.getInstance().get("engine_id")

// 移除缓存
FlutterEngineCache.getInstance().remove("engine_id")

但要注意,缓存只是存引用,不是深拷贝。你从缓存取出来的 Engine 还是原来那个。所以如果你在某个页面用完了,记得不要销毁它,否则其他页面就炸了。

我曾经遇到过一个线上问题:用户在 A 页面关闭了 Flutter 页面,我在 onDestroy 里调了 engine.destroy(),结果 B 页面再打开时直接崩溃。后来才发现,两个页面共用了同一个 Engine。嗯,从那以后我定了个规矩:谁创建,谁销毁;谁缓存,谁管理

4.4 生命周期管理

FlutterEngine 的生命周期,本质上和它的宿主 Activity/Fragment 绑定。但因为是单例,所以不能简单地在 onDestroy 里销毁。

我一般这样处理:

宿主状态 Engine 操作 说明
onCreate 获取或创建 Engine 从缓存取,取不到就创建
onResume 调用 engine.lifecycleChannel.appIsResumed() 通知 Flutter 端恢复
onPause 调用 engine.lifecycleChannel.appIsInactive() 通知 Flutter 端暂停
onStop 调用 engine.lifecycleChannel.appIsPaused() Flutter 端进入后台
onDestroy 不要销毁 Engine! 只释放当前页面的引用

你看,核心就是:Engine 本身不销毁,只通知它状态变化。这样既保证了性能,又不会出现野指针。

注意: 如果你在 onDestroy 里调了 engine.destroy(),那这个 Engine 就彻底废了。下次再用必须重新创建,单例的意义就没了。

4.5 内存优化:别让 Engine 成为内存黑洞

单例 Engine 虽然省了创建时间,但如果不注意,它可能成为内存泄漏的源头。我总结了几条实战经验:

  • 及时释放平台通道的监听器: 每个 Flutter 页面都会注册 MethodChannel,如果页面销毁了但监听器还在,就会持有 Activity 引用。我习惯在 onDestroy 里调用 channel.setMethodCallHandler(null)
  • 清理图片缓存: Flutter 的图片缓存默认是 1000 张。如果你的 App 图片很多,可以适当调小。在 Dart 端调用 PaintingBinding.instance.imageCache.clear()
  • 避免 Engine 持有 Context: 创建 Engine 时传入的 Context 最好是 Application 级别的。如果传了 Activity,那 Activity 销毁后 Engine 还持有它,就会泄漏。
  • 监控内存: 我一般在 Debug 版本里加一个定时器,每 5 秒打印一次 Engine 的 Dart 堆内存。如果发现异常增长,就及时排查。
// 在 Dart 端获取内存信息
import 'dart:developer' as developer;

void printMemoryUsage() {
  final info = developer.Service.getInfo();
  print('Current memory: ${info.currentRss}');
}

4.6 知识体系总览

下面这张图,是我自己总结的 FlutterEngine 管理全流程。你可以把它当作一个检查清单:

FlutterEngine 管理全流程 1. 创建 Engine Application.onCreate 2. 存入缓存 FlutterEngineCache 3. 页面使用 FlutterActivity 4. 生命周期同步 onResume/onPause 5. 页面释放 不销毁 Engine 内存优化要点 • 释放 MethodChannel 监听器 • 清理图片缓存 • 使用 Application Context • 监控 Dart 堆内存

说白了,FlutterEngine 的管理就三句话:全局只创建一次,缓存统一管理,生命周期只通知不销毁。做到这三点,你的 Flutter 页面就能既快又稳。

最后提醒一句: 如果你在项目中用了多个 FlutterEngine(比如同时打开两个 Flutter 页面),一定要确保它们各自有独立的缓存 key。否则取出来的 Engine 被两个页面同时操作,会出现各种诡异问题。我踩过这个坑,你最好别试。

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