Flutter 2 Router 從入門到放棄 - 基本使用、區別&優勢
本文轉載自微信公眾號「微醫大前端技術」,作者周建華。轉載本文請聯系微醫大前端技術公眾號。
前言
Flutter 2 主要帶來的新特性有 Null 安全性趨于穩定,桌面和 Web 支持也正式宣布進入 stable 渠道,最受大家關注的就是 Add-to-App 相關的更新,從而改善 Flutter2 之前的版本混合開發體驗不好缺點。所謂的 Add-to-App 就是將 Flutter 添加到現有的 iOS 和 Android 應用程序中來利用 Flutter,在兩個移動平臺上復用 Flutter 代碼同時仍保留現有本機代碼庫的絕佳方法。在此方案出現之前,類似的第三方支持有 flutter_boost 、flutter_thrio 等,但是這些方案都要面對的問題是:非官方的支持必然存在每個版本需要適配的問題,而按照 Flutter 目前更新的速度,很可能每個季度的版本都存在較大的變動,所以如果開發者不維護或者維護不及時,那么侵入性極強的這類框架很容易就成為項目的瓶頸。
Flutter2 多引擎混合開發基本用法
1、先創建一個 Android 原生工程,( Android 原生項目工程創建過程略過)
2、Android 項目創建引入 Flutter Module,使用 File -> New- > New Module … -> 選擇 Flutter Module ,然后指定一個 module name,填寫相關息,最后點擊確定,等待 Gradle sync 完成。
3、Android 項目集成 Flutter Module
1)創建 FlutterEngineGroup 對象,FlutterEngineGroup 可以用來管理多個 FlutterEngine 對象,多個 FlutterEngine 之間是可以共享資源的,這樣多個 FlutterEngine 占用的資源相對會少一些,FlutterEngineGroup 需要在 Application onCreate 方法中創建。
- package com.zalex.hybird;
- import android.app.Application;
- import io.flutter.embedding.engine.FlutterEngineGroup;
- public class WYApplication extends Application {
- public FlutterEngineGroup engineGroup;
- @Override
- public void onCreate() {
- super.onCreate();
- // 創建 FlutterEngineGroup 對象
- engineGroup = new FlutterEngineGroup(this);
- }
- }
2)創建 WYFlutterEngineManager 緩存管理類,通過 FlutterEngineCache 緩存類,先從中獲取緩存的 FlutterEngine,如果沒有取到,通過 findAppBundlePath 和 entrypoint 創建出 DartEntrypoint 對象,這里的 findAppBundlePath 主要就是默認的 flutter_assets 目錄;而 entrypoint 其實就是 dart 代碼里啟動方法的名稱;也就綁定了在 dart 中 runApp 的方法,再通過 createAndRunEngine 方法創建一個 FlutterEngine,然后緩存起來。
- public class WYFlutterEngineManager {
- public static FlutterEngine flutterEngine(Context context, String engineId, String entryPoint) {
- // 1. 從緩存中獲取 FlutterEngine
- FlutterEngine engine = FlutterEngineCache.getInstance().get(engineId);
- if (engine == null) {
- // 如果緩存中沒有 FlutterEngine
- // 1. 新建 FlutterEngine,執行的入口函數是 entryPoint
- WYApplication app = (WYApplication) context.getApplicationContext();
- DartExecutor.DartEntrypoint dartEntrypoint = new DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(), entryPoint);
- engine = app.engineGroup.createAndRunEngine(context, dartEntrypoint);
- // 2. 存入緩存
- FlutterEngineCache.getInstance().put(engineId, engine);
- }
- return engine;
- }
- }
Activity 綁定 flutter 引擎入口
- public class WYFlutterActivity extends FlutterActivity implements EngineBindingsDelegate {
- private WYFlutterBindings flutterBindings;
- @Override
- protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- flutterBindings = new WYFlutterBindings(this,SingleFlutterActivity.class.getSimpleName(),"main",this);
- flutterBindings.attach();
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- flutterBindings.detach();
- }
- @Override
- public FlutterEngine provideFlutterEngine(@NonNull @NotNull Context context) {
- return flutterBindings.getEngine();
- }
- @Override
- public void onNext() {
- Intent flutterIntent = new Intent(this, MainActivity.class);
- startActivity(flutterIntent);
- }
- }
Fragment 綁定 flutter 引擎入口
- int engineId = engineId ;//自定義引擎 Id
- int fragmentId = 1233444;//自定義 FragmentId
- FrameLayout flutterContainer = new FrameLayout(this);
- root.addView(flutterContainer);
- flutterContainer.setId(containerId);
- flutterContainer.setLayoutParams(new LinearLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT,
- 1.0f
- ));
- WYFlutterBindings flutterBindings = new WYFlutterBindings(this,"WYTopFragment","fragmentMain",this);
- FlutterEngine engine = bottomBindings.getEngine();
- FlutterEngineCache.getInstance().put(engineId+"", engine);
- Fragment flutterFragment =FlutterFragment.withCachedEngine(engineId+"").build();
- fragmentManager
- .beginTransaction()
- .add(containerId, flutterFragment)
- .commit();
3)flutter 模塊引擎入口綁定,除了 main 入口,其他引擎入口都需要加上@pragma('vm:entry-point')注解
- void main() => runApp(MyApp(Colors.blue));
- @pragma('vm:entry-point')
- void fragmentMain() => runApp(CustomApp(Colors.green));
Flutter2多引擎混合開發與單引擎混合開發比較
1、 Flutter 多引擎方案是 flutter api 一直都是可以支持的,可以創建多個引擎,也可以渲染多個不同的界面,也是獨立的,但是每次啟動一個 flutter 引擎,都會占一個獨立的引擎,通過測試可以發現,一個引擎 40M,創建 10 個引擎消耗了 235M,對內存的占用很大,在開發中是不可接受的。
2、由于 Flutter 2 之前版本多引擎的缺陷,業內的做法一般是對 isolate 或 engine 進行復用來解決。影響力較大的是以 FlutterBoost 和 Thrio 為代表的單引擎瀏覽器方案。即把 Activity/ViewController 作為承載 Dart 頁面的瀏覽器,在頁面切換時對單引擎進行 detach/attach,同時通知 Dart 層頁面切換,來實現 Engine 的復用。由于只持有了一個 Engine 單例,僅創建一份 isolate,Dart 層是通信和資源共享的,內存損耗也得以有顯著的降低。但是單引擎實現依賴于修改官方的 io.flutter 包,對 flutter 框架做出比較大的結構性修改。
3、從 Flutter 2 開始,多引擎下使用 FlutterEngineGroup 來創建新的 Engine,官方宣稱內存損耗僅占 180K,其本質是使 Engine 可以共享 GPU 上下文、字形和 isolate group snapshot,從而實現了更快的初始速度和更低的內存占用。
4、Flutter 2 與 Flutter 1 創建引擎的區別:
Flutter1 引擎創建
- //Android
- val engine = FlutterEngine(this)
- engine.dartExecutor.executeDartEntrypoin(DartExecutor.DartEntrypoint.createDefault())
- FlutterEngineCache.getInstance().put(1,engine)
- val intent = FlutterActivity.withCacheEngine(1).build(this)
- //iOS
- let engine = FlutterEngine()
- engine.run()
- let vc = FlutterViewController(engine:engine,nibName:nil,bundle:nil)
Fluter2 引擎創建
- //Android
- val engineGroup = FlutterEngineGroup(context)
- val engine1 = engineGroup.createAndRunDefaultEngine(context)
- val engine2 = engineGroup.createAndRunEngine(context,DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(),"anotherEntrypoint"))
- //iOS
- let engineGroup = FlutterEngineGroup(name:"example",project:nil)
- let engine1 = engineGroup.makeEngine(withEntrypoint:nil,libraryURI:nil)
- let engine2 = engineGroup.makeEngine(withEntrypoint:"anotherEntrypoint",libraryURI:nil)
5、Flutter 混合開發方案比較
6、Flutter 輕量級多引擎和單引擎優缺點比較
后記
本文通過代碼和表格,我們講述了 Flutter 2 多引擎使用、多引擎混合開發與單引擎混合開發區別和優缺點比較,下一節我們將一起去學習 Flutter 2 多引擎的實現原理。