美文网首页
FlutterBoost源码解析

FlutterBoost源码解析

作者: 刺客的幻影 | 来源:发表于2020-07-08 22:32 被阅读0次

    项目中在使用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页面的主体流程就走完了,思路也更清晰了一些

    相关文章

      网友评论

          本文标题:FlutterBoost源码解析

          本文链接:https://www.haomeiwen.com/subject/dnhtcktx.html