FlutterBoost源碼解析

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

FlutterBoost解決的混合開發(fā)過程中的幾個痛點:

  • 統(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)鍵類的層級關(guān)系

image.png

flutter跳轉(zhuǎn)native過程比較簡單清晰尿招,下面分析下flutter跳轉(zhuǎn)flutter的整個流程

跳轉(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(點進(jìn)去發(fā)現(xiàn)是對MethodChannel的一個包裝)的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那邊對應(yīng)的注冊位置割去,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();
                }
            }
        }
    }

來到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,其實現(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對象的OpenContainer,而這個router具體實現(xiàn),則在我們構(gòu)造Platform對象時生成的

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

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

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

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

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
接下來ContainerRecord中的MethodChannelProxy是關(guān)鍵

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

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

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

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

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

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

那么在Dart端手负,是在哪里接收的呢蝠猬?我們查一下flutter里注冊的channel,發(fā)現(xiàn)果然存在一個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ù)往下走,可以看到另外一個關(guān)鍵類:ContainerCoordinator 棒搜,里面注冊了了很多方法回調(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>(() {});
  }

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

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

然后就在_pageBuilders里去找媚媒,有沒有name對應(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;
  }

那么萄凤,這個pageBuilders又是什么噩死,何時賦值的呢?我們再回頭看看dart端flutter_boost路由注冊的地方

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

這個我們一般在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頁面的主體流程就走完了痴奏,思路也更清晰了一些

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末暑诸,一起剝皮案震驚了整個濱河市笛洛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瘦材,老刑警劉巖簿晓,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機奶陈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門事示,熙熙樓的掌柜王于貴愁眉苦臉地迎上來劝堪,“玉大人,你說我怎么就攤上這事。” “怎么了答渔?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵嗦明,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任虱岂,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己褐健,他們只是感情好俊抵,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布谎替。 她就那樣靜靜地躺著,像睡著了一般淋肾。 火紅的嫁衣襯著肌膚如雪碌尔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天测蹲,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的粥帚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼查描,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了匠襟?” 一聲冷哼從身側(cè)響起忽舟,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎状您,沒想到半個月后膏孟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弊决,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡仇奶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年蹭秋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片志电。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡曙咽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挑辆,到底是詐尸還是另有隱情例朱,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布之拨,位于F島的核電站茉继,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蚀乔。R本人自食惡果不足惜烁竭,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吉挣。 院中可真熱鬧派撕,春花似錦、人聲如沸睬魂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氯哮。三九已至际跪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姆打。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工良姆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幔戏。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓玛追,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闲延。 傳聞我的和親對象是個殘疾皇子痊剖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353