End

多个 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

posted @ 2022-06-04 00:34  白乾涛  阅读(1997)  评论(0编辑  收藏  举报