4、FlutterEngine的创建与管理:创建单例FlutterEngine、缓存策略、生命周期管理、内存优化
FlutterEngine 这东西,说白了就是 Flutter 在原生世界里的「发动机」。你把它启动起来,Dart 代码才能跑,UI 才能渲染。但很多新手容易犯一个错——每次打开 Flutter 页面都 new 一个 Engine。我刚开始也这么干过,结果内存直接飙到 200MB+,用户反馈说 App 越用越卡。嗯,今天我们就来聊聊怎么管好这个 Engine。
4.1 为什么需要单例 FlutterEngine?
你想想看,一个 FlutterEngine 创建出来,背后要做多少事:
- 加载 Dart VM(虚拟机)
- 初始化 Flutter 框架
- 加载 Dart 业务代码(AOT 或 JIT)
- 创建渲染管线、平台通道
这些操作加起来,少说也要几百毫秒。如果你每次打开页面都重新创建,用户就会看到白屏、卡顿。更糟糕的是,多个 Engine 同时存在,内存直接翻倍。
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 里完全没问题。
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 本身不销毁,只通知它状态变化。这样既保证了性能,又不会出现野指针。
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 的管理就三句话:全局只创建一次,缓存统一管理,生命周期只通知不销毁。做到这三点,你的 Flutter 页面就能既快又稳。
公众号:蓝海资料掘金营,微信deep3321