多个 Flutter 页面 混合路由栈 FlutterEngineGroup
目录
多个 Flutter 页面
适用场景:
- 混合路由栈:Flutter 和 native 互跳,即 native -> Flutter -> native -> Flutter
- 模块化:使用多个 Flutter 实例,每个实例各自维护路由栈、UI 和应用状态
- 多视图:多个 Flutter View 同时集成在同一个页面上,且同时显示
额外增加的 Flutter 实例只会增加约
180K
的内存占用
FlutterEngineGroup 的特性
由同一 FlutterEngineGroup
生成的 FlutterEngine
可共享常用的系统资源,例如 GPU 上下文、字体度量(font metrics)、隔离线程的快照(isolate group snapshot),的性能优势(performance advantage),从而加快首次渲染的速度、降低延迟并降低内存占用。
Represents a collection of FlutterEngines who
share resources
to allow them to be created faster and with less memory than calling the FlutterEngine's constructor multiple times.
When creating or recreating the first FlutterEngine in the FlutterEngineGroup, the behavior is the same as creating a FlutterEngine via its constructor. When subsequent FlutterEngines are created, resources from an existing living FlutterEngine is re-used.
The shared resources are kept until the last surviving FlutterEngine is destroyed.
Deleting a FlutterEngineGroup doesn't invalidate its existing FlutterEngines, but it eliminates the possibility to create more FlutterEngines in that group.
- 通过 FEG 生成的 FE 可以关联 UI 相关的类,例如
FlutterActivity
,这与构造缓存的 FE 类似 - 通过 FEG 生成的首个 FE 不需要保活,只要有 1 个可用的 FE,就可以随时在各个 FE 之间共享资源
- 通过 FEG 生成的首个 FE 与先前使用构造方法构造的 FE 有相同的性能特征
- 当由 FEG 构造的所有 FE 都被销毁后,下一个创建的 FE 与首个创造的 FE 有相同的性能特征
- FEG 本身不需要保活,将其销毁后,已生成的 FE 不受影响,但无法继续在现有共享的基础上创建新 FE
FlutterEngineGroup 案例
MultFlutterTestActivity
class MultFlutterTestActivity : ListActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val array = arrayOf(
"addObserver",
"removeObserver",
"SingleFlutterActivity",
"DoubleFlutterActivity",
"get",
"set",
)
listAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, array)
}
@Deprecated("Deprecated in Java")
override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
when (position) {
0 -> DataModel.addObserver(::observer)
1 -> DataModel.removeObserver(this::observer)
2 -> startActivity(Intent(this, SingleFlutterActivity::class.java))
3 -> startActivity(Intent(this, DoubleFlutterActivity::class.java))
4 -> Toast.makeText(this, "data: ${DataModel.data}", Toast.LENGTH_SHORT).show()
5 -> DataModel.data += 1
}
}
private fun observer(data: Int): Unit = Toast.makeText(this, "observer: $data", Toast.LENGTH_SHORT).show()
}
DataModel
// A singleton/observable data model for the data shared between Flutter and the host platform.
object DataModel {
private val observers: MutableList<WeakReference<(Int) -> Unit>> = mutableListOf()
var data = 0
set(value) {
field = value
observers.mapNotNull { it.get() }.forEach { it.invoke(value) }
}
fun addObserver(observer: (Int) -> Unit) = observers.add(WeakReference(observer))
fun removeObserver(observer: (Int) -> Unit): Boolean = observers.removeIf {
if (it.get() != null) it.get() == observer else true
}
}
EngineBindings 【核心】
class EngineBindings(context: Context, val delegate: EngineBindingsDelegate, entrypoint: String, val id: Int) {
val channel: MethodChannel
val engine: FlutterEngine
init {
// Represents a collection of FlutterEngines who share resources to allow them to be created faster and with less memory
val engineGroup: FlutterEngineGroup = (context.applicationContext as MyApplication).engineGroup // 全局的 FEG
// The path within the AssetManager where the app will look for assets
val pathToBundle: String = FlutterInjector.instance().flutterLoader().findAppBundlePath()
// This has to be lazy to avoid creation before the FlutterEngineGroup
val dartEntrypoint = DartExecutor.DartEntrypoint(pathToBundle, entrypoint)
// Creates a FlutterEngine in this group and run its DartExecutor with the specified DartEntrypoint
engine = engineGroup.createAndRunEngine(context, dartEntrypoint) // 创建 FlutterEngine 并执行指定的 DartEntrypoint
channel = MethodChannel(engine.dartExecutor.binaryMessenger, "multiple-flutters")
}
fun attach() {
DataModel.addObserver(::observer)
channel.invokeMethod("setData", DataModel.data) // 把当前的值传给 Dart 端
channel.setMethodCallHandler { call, result ->
when (call.method) {
"incrementCount" -> result.success(true).also { DataModel.data = DataModel.data + 1 }
"next" -> result.success(true).also { delegate.onNext() }
else -> result.notImplemented()
}
}
}
fun detach() {
engine.destroy() // Cleans up all components within this FlutterEngine and destroys the associated Dart Isolate
DataModel.removeObserver(::observer)
channel.setMethodCallHandler(null)
}
private fun observer(data: Int): Unit = channel.invokeMethod("setData", data)
}
SingleFlutterActivity
class SingleFlutterActivity : FlutterActivity(), EngineBindingsDelegate {
// EngineBindings is used to bridge communication between the Flutter instance and the DataModel
private val engineBindings: EngineBindings by lazy { EngineBindings(this, this, "main", 1) }
override fun onCreate(bundle: Bundle?) = super.onCreate(bundle).also { engineBindings.attach() }
override fun onDestroy() = super.onDestroy().also { engineBindings.detach() }
override fun provideFlutterEngine(context: Context): FlutterEngine = engineBindings.engine // 使用指定的 FlutterEngine
override fun onNext() = startActivity(Intent(this, MultFlutterTestActivity::class.java))
}
DoubleFlutterActivity
class DoubleFlutterActivity : FragmentActivity(), EngineBindingsDelegate {
private val topBindings: EngineBindings by lazy { EngineBindings(this, this, "topMain", 2) }
private val bottomBindings: EngineBindings by lazy { EngineBindings(this, this, "bottomMain", 3) }
private lateinit var root: LinearLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
root = LinearLayout(this).apply { orientation = LinearLayout.VERTICAL }
setContentView(root)
addFlutterFragment(topBindings)
addFlutterFragment(bottomBindings)
topBindings.attach()
bottomBindings.attach()
}
private fun addFlutterFragment(engineBindings: EngineBindings) {
val containerViewId: Int = engineBindings.id
val engineId: String = engineBindings.id.toString()
FrameLayout(this).apply {
id = containerViewId
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0f)
root.addView(this)
}
FlutterEngineCache.getInstance().put(engineId, engineBindings.engine) // 缓存 FlutterEngine
val flutterFragment = FlutterFragment.withCachedEngine(engineId).build<FlutterFragment>() // 构建 Fragment
supportFragmentManager.beginTransaction().add(containerViewId, flutterFragment).commit() // 添加 Fragment
}
override fun onDestroy() {
FlutterEngineCache.getInstance().remove(topBindings.id.toString())
FlutterEngineCache.getInstance().remove(bottomBindings.id.toString())
super.onDestroy()
bottomBindings.detach()
topBindings.detach()
}
override fun onNext() = startActivity(Intent(this, MultFlutterTestActivity::class.java))
}
创建 FlutterEngineGroup
class MyApplication : Application() {
lateinit var engineGroup: FlutterEngineGroup // Create a FlutterEngineGroup whose child engines will share resources
override fun onCreate() {
super.onCreate()
engineGroup = FlutterEngineGroup(this)
}
}
一个回调接口
interface EngineBindingsDelegate {
fun onNext()
}
Flutter 端代码
dependencies:
flutter:
sdk: flutter
url_launcher: ^6.0.6
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart' as launcher;
void main() => runApp(const MyApp(color: Colors.red));
@pragma('vm:entry-point')
void topMain() => runApp(const MyApp(color: Colors.green));
@pragma('vm:entry-point')
void bottomMain() => runApp(const MyApp(color: Colors.blue));
class MyApp extends StatelessWidget {
const MyApp({super.key, required this.color});
final MaterialColor color;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: color),
home: const MyHomePage(title: '演示 MultFlutter'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final _url = Uri.parse('https://www.cnblogs.com/baiqiantao/');
final MethodChannel _channel = const MethodChannel('multiple-flutters');
@override
void initState() {
super.initState();
_channel.setMethodCallHandler((call) async {
if (call.method == "setData") {
_counter = call.arguments as int;
setState(() => _counter);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('当前的值为:$_counter'),
ElevatedButton(
child: const Text('将当前值 +1'),
onPressed: () => _channel.invokeMethod<void>("incrementCount", _counter),
),
ElevatedButton(
child: const Text('跳到一个 native 页面'),
onPressed: () => _channel.invokeMethod<void>("next", _counter),
),
ElevatedButton(
child: const Text('使用浏览器打开一个 url'),
onPressed: () async {
if (await launcher.canLaunchUrl(_url)) {
await launcher.launchUrl(_url);
}
},
),
],
),
),
);
}
}
2022-6-4
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/16341100.html