FlutterBoost源码解析

项目中在使用FlutterBoost,对其实现原理比较好奇,看了一下,关键的类有点多,自己捋一捋,记录一下,便于理解

FlutterBoost解决的混合开发过程中的几个痛点:

  • 统一了native和flutter之间跳转方式
  • 提供与native一致的生命周期管理
  • 优化FlutterEngine的使用,减少内存消耗
  • 其他(比如黑屏闪屏的坑)

关系详解:todo

主要的结构图


image.png

关键类的作用 todo
关键类的层级关系

image.png

flutter跳转native过程比较简单清晰,下面分析下flutter跳转flutter的整个流程

跳转入口

 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);
 }

调用了BoostChannel(点进去发现是对MethodChannel的一个包装)的invokeMethod方法,最终是调用的MethodChannel的invokeMethod,指向的’openPage‘

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

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

找一下native那边对应的注册位置,FlutterBoostPlugin$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);
    }

调用的是Platform类的openContainer,其实现在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);
                }

        }

最终调用到router对象的OpenContainer,而这个router具体实现,则在我们构造Platform对象时生成的

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

继续往下看,openPagerByUrl方法, 通过BoostFlutterActivity的一系列构造,生成了一个intent

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

而这个intent指向的目标Activity其实就是BoostFlutterActivity,在调用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是关键

那么,intent里面配置的这些关键参数在哪里使用到了呢?看看FlutterActivityAndFragmentDeleg
ate里的onCreateView方法中调用的syncer关键类,从名字和里面的内容可以看得出,它的作用主要用来做生命周期同步的

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

具体实现在ContainerRecord的onCreate中,并且运用了一个代理模式,最终调用到了这一个方法

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

继续往下深入几层,可以看到注册了一个名为“flutter_booost”的MethodChannel,将方法名和参数传到了这里

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

那么在Dart端,是在哪里接收的呢?我们查一下flutter里注册的channel,发现果然存在一个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分支,再继续往下走,可以看到另外一个关键类:ContainerCoordinator ,里面注册了了很多方法回调,包括我们要找的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>(() {});
  }

这里面有个关键方法_nativeContainerDidInit,

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

然后就在_pageBuilders里去找,有没有name对应的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跳转flutter页面的主体流程就走完了,思路也更清晰了一些

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342