在 Flutter 中,单引擎和多引擎是两种不同的应用开发模式,它们在性能、资源管理和使用场景等方面存在差异。
单引擎模式
单引擎模式是指在一个 Flutter 应用中只使用一个 Flutter 引擎实例。这个引擎负责处理所有 Flutter 界面的渲染、动画、输入事件等。大多数简单的 Flutter 应用默认采用单引擎模式。
-
优点
-
资源占用少:由于只使用一个引擎实例,内存和 CPU 等系统资源的占用相对较少,有助于提高应用的性能和电池续航能力。
-
状态管理简单:整个应用的状态管理相对简单,因为所有的 Flutter 界面都在同一个引擎上下文中运行,数据共享和状态同步更加容易。
-
启动速度快:单引擎模式下,应用启动时只需初始化一个引擎实例,启动速度相对较快。
-
-
缺点
-
灵活性差:如果应用需要在不同的界面或模块之间进行复杂的隔离和交互,单引擎模式可能无法满足需求。
-
稳定性受影响:一旦引擎出现问题,整个应用可能会受到影响。
-
多引擎模式
多引擎模式是指在一个应用中使用多个 Flutter 引擎实例。每个引擎实例可以独立运行,负责不同的 Flutter 界面或模块。多引擎模式通常用于复杂的应用场景,如在一个原生应用中嵌入多个独立的 Flutter 页面。
-
优点
-
隔离性好:不同的引擎实例之间相互隔离,一个引擎出现问题不会影响其他引擎的运行,提高了应用的稳定性。
-
灵活性高:可以根据需要独立控制每个引擎的生命周期、状态和资源,实现更复杂的界面和交互逻辑。
-
与原生集成更方便:在原生应用中嵌入多个独立的 Flutter 页面时,多引擎模式可以更好地与原生代码进行集成。
-
-
缺点
-
资源占用多:每个引擎实例都需要占用一定的系统资源,多个引擎实例会增加内存和 CPU 的负担,可能影响应用的性能和电池续航能力。
-
状态管理复杂:不同引擎实例之间的数据共享和状态同步相对复杂,需要额外的机制来实现。
-
多引擎的典型场景
- 同一页面嵌入多个 Flutter 视图 例如:一个原生 Android/iOS 页面中同时显示两个独立的 Flutter 界面。
- 多模块隔离 不同 Flutter 模块需要独立的状态管理或插件环境。
- 混合栈管理 在原生和 Flutter 混合导航时,为不同页面分配独立引擎以避免状态冲突。
多引擎方案 FlutterEngineGroup
简单使用
1. Android 平台
使用 FlutterEngineGroup
(推荐)或手动创建多个 FlutterEngine
实例。
// 使用 FlutterEngineGroup 创建引擎
val engineGroup = FlutterEngineGroup(context)
val engine1 = engineGroup.createAndRunEngine(
context, DartExecutor.DartEntrypoint.createDefault()
)
val engine2 = engineGroup.createAndRunEngine(
context, DartExecutor.DartEntrypoint.createDefault()
)
// 将引擎绑定到 FlutterView
val flutterView1 = FlutterView(context).apply {
attachToFlutterEngine(engine1)
}
val flutterView2 = FlutterView(context).apply {
attachToFlutterEngine(engine2)
}
2. iOS 平台
使用 FlutterEngineGroup
(iOS 13+)或手动创建多个 FlutterEngine
。
// 创建引擎组
let engineGroup = FlutterEngineGroup(name: "group", project: nil)
// 生成引擎实例
let engine1 = engineGroup.makeEngine(withEntrypoint: "main", libraryURI: nil)
let engine2 = engineGroup.makeEngine(withEntrypoint: "main", libraryURI: nil)
// 绑定到 FlutterViewController
let flutterVC1 = FlutterViewController(engine: engine1, nibName: nil, bundle: nil)
let flutterVC2 = FlutterViewController(engine: engine2, nibName: nil, bundle: nil)
3. Flutter 侧配置
每个引擎默认独立运行,需确保入口点(main()
)支持多实例:
void main() {
runApp(MyApp()); // 确保无全局静态状态冲突
}
注意事项
- 1、内存开销:每个引擎实例约占用 5-20MB 内存,需根据设备性能合理控制引擎数量。
- 2、插件注册:每个引擎需单独注册插件(通过
GeneratedPluginRegistrant
)。若插件依赖原生上下文(如相机),需确保多引擎下的兼容性。 - 3、生命周期管理:在页面销毁时调用
destroy()
释放引擎 - 4、共享数据:不同引擎间无法直接共享内存,需通过原生层中转(如
MethodChannel
或EventChannel
)。
引擎池(iOS端)
class FlutterEnginePool {
// MARK: - 单例
static let shared = FlutterEnginePool()
private init() {}
// MARK: - 核心属性
private let engineGroup = FlutterEngineGroup(name: "flutter_engine_pool", project: nil)
private var activeEngines: [String: FlutterEngine] = [:] // 使用中的引擎 [路由标识: 引擎]
private var idleEngines: [String: FlutterEngine] = [:] // 闲置引擎池
private let queue = DispatchQueue(label: "com.flutter.engine.pool.lock") // 线程安全队列
// MARK: - 配置参数
private let maxIdleCount = 3 // 最大闲置引擎数
private let maxIdleTime: TimeInterval = 300 // 闲置超时时间(秒)
private var timer: Timer? // 闲置检测定时器
// MARK: - 公开方法
/// 获取引擎(按路由标识)
func getEngine(for route: String) -> FlutterEngine {
return queue.sync {
// 1. 查找可用闲置引擎
if let engine = idleEngines.removeValue(forKey: route) {
activeEngines[route] = engine
return engine
}
// 2. 创建新引擎
let engine = engineGroup.makeEngine(
withEntrypoint: "main",
libraryURI: nil,
initialRoute: route
)
GeneratedPluginRegistrant.register(with: engine)
activeEngines[route] = engine
// 3. 启动闲置检测
startIdleCheckTimer()
return engine
}
}
/// 归还引擎到池中
func recycleEngine(for route: String) {
queue.async {
guard let engine = self.activeEngines.removeValue(forKey: route) else { return }
// 1. 超过最大闲置数时销毁最旧的引擎
if self.idleEngines.count >= self.maxIdleCount, let firstKey = self.idleEngines.keys.first {
self.idleEngines.removeValue(forKey: firstKey)?.destroyContext()
}
// 2. 记录闲置时间戳(用于LRU回收)
let metadata = ["recycleTime": Date()]
engine.setMetadata(metadata)
// 3. 存入闲置池
self.idleEngines[route] = engine
}
}
/// 强制销毁所有引擎(用于内存警告)
func purgeAllEngines() {
queue.async {
self.activeEngines.values.forEach { $0.destroyContext() }
self.idleEngines.values.forEach { $0.destroyContext() }
self.activeEngines.removeAll()
self.idleEngines.removeAll()
}
}
// MARK: - 私有方法
private func startIdleCheckTimer() {
guard timer == nil else { return }
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
self?.checkIdleEngines()
}
}
private func checkIdleEngines() {
queue.async {
let now = Date()
// 清理超时引擎
self.idleEngines = self.idleEngines.filter { route, engine in
guard let metadata = engine.metadata as? [String: Any],
let recycleTime = metadata["recycleTime"] as? Date else {
return false
}
if now.timeIntervalSince(recycleTime) > self.maxIdleTime {
engine.destroyContext()
return false
}
return true
}
}
}
}
// MARK: - 内存警告扩展
extension FlutterEnginePool {
func setupMemoryWarningObserver() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleMemoryWarning),
name: UIApplication.didReceiveMemoryWarningNotification,
object: nil
)
}
@objc private func handleMemoryWarning() {
purgeAllEngines()
}
}
在 App 启动时初始化
// AppDelegate.swift
@main
class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// 初始化引擎池并启动内存监听
FlutterEnginePool.shared.setupMemoryWarningObserver()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
在原生界面中跳转 Flutter
class NativeViewController: UIViewController {
func navigateToFlutterPage(route: String) {
// 获取引擎
let engine = FlutterEnginePool.shared.getEngine(for: route)
let flutterVC = FlutterViewController(engine: engine, nibName: nil, bundle: nil)
// 跳转页面
navigationController?.pushViewController(flutterVC, animated: true)
}
func onReturnFromFlutter() {
// 归还引擎(通常在 viewDidDisappear 或 Flutter 侧触发返回时调用)
FlutterEnginePool.shared.recycleEngine(for: "your_route_identifier")
}
}
Flutter 侧返回通知
// 在 Flutter 页面中添加返回按钮逻辑
ElevatedButton(
onPressed: () {
Navigator.pop(context); // 退出当前页面
// 通知 iOS 归还引擎
const channel = MethodChannel('com.example/engine_channel');
channel.invokeMethod('recycleEngine');
},
child: Text('返回原生'),
)
// iOS 端监听(在 AppDelegate 中)
MethodChannel(name: "com.example/engine_channel", binaryMessenger: engine.binaryMessenger)
.setMethodCallHandler { call, _ in
if call.method == "recycleEngine" {
FlutterEnginePool.shared.recycleEngine(for: "your_route_identifier")
}
}