FlutterBoost源碼解析

項(xiàng)目中在使用FlutterBoost,對(duì)其實(shí)現(xiàn)原理比較好奇,看了一下,關(guān)鍵的類有點(diǎn)多,自己捋一捋,記錄一下,便于理解

FlutterBoost解決的混合開(kāi)發(fā)過(guò)程中的幾個(gè)痛點(diǎn):

  • 統(tǒng)一了native和flutter之間跳轉(zhuǎn)方式
  • 提供與native一致的生命周期管理
  • 優(yōu)化FlutterEngine的使用,減少內(nèi)存消耗
  • 其他(比如黑屏閃屏的坑)

關(guān)系詳解:todo

主要的結(jié)構(gòu)圖


image.png

關(guān)鍵類的作用 todo
關(guān)鍵類的層級(jí)關(guān)系

image.png

flutter跳轉(zhuǎn)native過(guò)程比較簡(jiǎn)單清晰,下面分析下flutter跳轉(zhuǎn)flutter的整個(gè)流程

跳轉(zhuǎn)入口

 FlutterBoost.singleton.open(RouterConstants.USER_INFO_PAGE);

看一下open做了什么

Future<Map<dynamic, dynamic>> open(String url,
     {Map<dynamic, dynamic> urlParams, Map<dynamic, dynamic> exts}) {
   Map<dynamic, dynamic> properties = new Map<dynamic, dynamic>();
   properties["url"] = url;
   properties["urlParams"] = urlParams;
   properties["exts"] = exts;
   return channel.invokeMethod<Map<dynamic, dynamic>>('openPage', properties);
 }

調(diào)用了BoostChannel(點(diǎn)進(jìn)去發(fā)現(xiàn)是對(duì)MethodChannel的一個(gè)包裝)的invokeMethod方法,最終是調(diào)用的MethodChannel的invokeMethod,指向的’openPage‘

  Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
    assert(method != "__event__");

    return _methodChannel.invokeMethod<T>(method, arguments);
  }

找一下native那邊對(duì)應(yīng)的注冊(cè)位置,F(xiàn)lutterBoostPlugin$BoostMethodHandler

 class BoostMethodHandler implements MethodChannel.MethodCallHandler {

        @Override
        public void onMethodCall(MethodCall methodCall, final MethodChannel.Result result) {

            FlutterViewContainerManager mManager = (FlutterViewContainerManager) FlutterBoost.instance().containerManager();
            switch (methodCall.method) {
               ...
                case "openPage": {
                    try {
                        Map<String, Object> params = methodCall.argument("urlParams");
                        Map<String, Object> exts = methodCall.argument("exts");
                        String url = methodCall.argument("url");

                        mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
                            @Override
                            public void onResult(Map<String, Object> rlt) {
                                if (result != null) {
                                    result.success(rlt);
                                }
                            }
                        });
                    } catch (Throwable t) {
                        result.error("open page error", t.getMessage(), Log.getStackTraceString(t));
                    }
                }
                break;
               ...
                default: {
                    result.notImplemented();
                }
            }
        }
    }

來(lái)到FlutterViewContainerManager里的OpenContainer方法

 void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) {
      ...
       FlutterBoost.instance().platform().openContainer(context,url,urlParams,requestCode,exts);
    }

調(diào)用的是Platform類的openContainer,其實(shí)現(xiàn)在Flutterboost的build方法中

 public Platform build() {

            Platform platform = new Platform() {
                public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
                    router.openContainer(context, url, urlParams, requestCode, exts);
                }

        }

最終調(diào)用到router對(duì)象的OpenContainer,而這個(gè)router具體實(shí)現(xiàn),則在我們構(gòu)造Platform對(duì)象時(shí)生成的

INativeRouter router = (context, url, urlParams, requestCode, exts) -> {
            String assembleUrl = Utils.assembleUrl(url, urlParams);
            PageRouter.openPageByUrl(context, assembleUrl, urlParams);
        };

繼續(xù)往下看,openPagerByUrl方法, 通過(guò)BoostFlutterActivity的一系列構(gòu)造,生成了一個(gè)intent

   Intent intent = BoostFlutterActivity
                        .withNewEngine()
                        .url(pageName.get(path))
                        .params(params)
                        .backgroundMode(BoostFlutterActivity.BackgroundMode.opaque)
                        .build(context);

而這個(gè)intent指向的目標(biāo)Activity其實(shí)就是BoostFlutterActivity,在調(diào)用withNewEngine時(shí)賦值

SerializableMap serializableMap = new SerializableMap();
            serializableMap.setMap(params);

            return new Intent(context, activityClass)
                    .putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
                    .putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false)
                    .putExtra(EXTRA_URL, url)
                    .putExtra(EXTRA_PARAMS, serializableMap);




    public static NewEngineIntentBuilder withNewEngine() {
        return new NewEngineIntentBuilder(BoostFlutterActivity.class);
    }

ContainerCoordinator
接下來(lái)ContainerRecord中的MethodChannelProxy是關(guān)鍵

那么,intent里面配置的這些關(guān)鍵參數(shù)在哪里使用到了呢?看看FlutterActivityAndFragmentDeleg
ate里的onCreateView方法中調(diào)用的syncer關(guān)鍵類,從名字和里面的內(nèi)容可以看得出,它的作用主要用來(lái)做生命周期同步的

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
      ...
      mSyncer.onCreate();
      return flutterSplashView;
  }

具體實(shí)現(xiàn)在ContainerRecord的onCreate中,并且運(yùn)用了一個(gè)代理模式,最終調(diào)用到了這一個(gè)方法

 private void create() {
            if (mState == STATE_UNKNOW) {
                invokeChannelUnsafe("didInitPageContainer",
                        mContainer.getContainerUrl(),
                        mContainer.getContainerUrlParams(),
                        mUniqueId
                );
                //Debuger.log("didInitPageContainer");
                mState = STATE_CREATED;
            }
        }

繼續(xù)往下深入幾層,可以看到注冊(cè)了一個(gè)名為“flutter_booost”的MethodChannel,將方法名和參數(shù)傳到了這里

mMethodChannel = new MethodChannel(registrar.messenger(), "flutter_boost");

那么在Dart端,是在哪里接收的呢?我們查一下flutter里注冊(cè)的channel,發(fā)現(xiàn)果然存在一個(gè)BoostChannel

class BoostChannel {
  final MethodChannel _methodChannel = MethodChannel("flutter_boost");

  final Map<String, List<EventListener>> _eventListeners = Map();
  final Set<MethodHandler> _methodHandlers = Set();

  BoostChannel() {
    _methodChannel.setMethodCallHandler((MethodCall call) {
      if (call.method == "__event__") {
        String name = call.arguments["name"];
        Map arg = call.arguments["arguments"];
        List<EventListener> list = _eventListeners[name];
        if (list != null) {
          for (EventListener l in list) {
            l(name, arg);
          }
        }
      } else {
        for (MethodHandler handler in _methodHandlers) {
          handler(call);
        }
      }

      return Future<dynamic>.value();
    });
  }
}

由于方法名稱是didInitPageContainer,所以走的else分支,再繼續(xù)往下走,可以看到另外一個(gè)關(guān)鍵類:ContainerCoordinator ,里面注冊(cè)了了很多方法回調(diào),包括我們要找的didInitPageContainer


  Future<dynamic> _onMethodCall(MethodCall call) {
    Logger.log("onMetohdCall ${call.method}");

    switch (call.method) {
      case "didInitPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          _nativeContainerDidInit(pageName, params, uniqueId);
        }
        break;
        ...
    }

    return Future<dynamic>(() {});
  }

這里面有個(gè)關(guān)鍵方法_nativeContainerDidInit,

bool _nativeContainerDidInit(String name, Map params, String pageId) {
    performContainerLifeCycle(_createContainerSettings(name, params, pageId),
        ContainerLifeCycle.Init);
    return true;
  }

然后就在_pageBuilders里去找,有沒(méi)有name對(duì)應(yīng)的pageBuilder

 BoostContainerSettings _createContainerSettings(
      String name, Map params, String pageId) {
    Widget page;

    final BoostContainerSettings routeSettings = BoostContainerSettings(
        uniqueId: pageId,
        name: name,
        params: params,
        builder: (BuildContext ctx) {
          //Try to build a page using keyed builder.
          if (_pageBuilders[name] != null) {
            page = _pageBuilders[name](name, params, pageId);
          }

          //Build a page using default builder.
          if (page == null && _defaultPageBuilder != null) {
            page = _defaultPageBuilder(name, params, pageId);
          }

          assert(page != null);
          Logger.log('build widget:$page for page:$name($pageId)');

          return page;
        });

    return routeSettings;
  }

那么,這個(gè)pageBuilders又是什么,何時(shí)賦值的呢?我們?cè)倩仡^看看dart端flutter_boost路由注冊(cè)的地方

 ///Register a map builders
  void registerPageBuilders(Map<String, PageBuilder> builders) {
    ContainerCoordinator.singleton.registerPageBuilders(builders);
  }

這個(gè)我們一般在dart的main.dart里初始化

FlutterBoost.singleton.registerPageBuilders({
    RouterConstants.MINE_PAGE: (pageName, params, _) {
      return MinePage(params);
    },
    RouterConstants.USER_INFO_PAGE: (pageName, params, _) {
      return UserInfoPage(params);
    }
  });

這樣,flutter跳轉(zhuǎn)flutter頁(yè)面的主體流程就走完了,思路也更清晰了一些

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。