ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

多个 Flutter 页面 混合路由栈 FlutterEngineGroup

2022-06-04 01:02:18  阅读:217  来源: 互联网

标签:val FlutterEngineGroup override fun data Flutter 路由


本文地址


目录

目录

多个 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 或 FlutterViewController,与构造缓存的 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

标签:val,FlutterEngineGroup,override,fun,data,Flutter,路由
来源: https://www.cnblogs.com/baiqiantao/p/16341100.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有