美文网首页
hybrid_stack_manager浅析

hybrid_stack_manager浅析

作者: Zeit丶 | 来源:发表于2019-06-19 15:52 被阅读0次

    在混合工程中,flutter和原生之间的页面栈如何管理呢,由于我们采用的是hybrid_stack_manager,这里简单分析下整个流程

    一、原生跳Flutter

    1、使用

    原生

    HashMap<String,Object> m = new HashMap<>();
    m.put("flutter", true);
    HashMap<String,Object> p = new HashMap<>();
    p.put("title", "测试");
    XURLRouter.sharedInstance().openUrlWithQueryAndParams("hrd://test", m, p);
    

    Flutter

    Router.sharedInstance().routerWidgetHandler =
        ({RouterOption routeOption, Key key}) {
      print(routeOption.url);
      print(routeOption.query);
      print(routeOption.params);
      switch (routeOption.url) {
        case "hrd://test":
          return MaterialApp(...);
      }
      return null;
    };
    
    2、源码

    XURLRouter是个单例,先看openUrlWithQueryAndParams方法

    public boolean openUrlWithQueryAndParams(String url, HashMap<String, Object> query, HashMap<String, Object> params){
        Uri tmpUri = Uri.parse(url);
        if(!kOpenUrlPrefix.equals(tmpUri.getScheme()))
            return false;
        //会走这个if
        if(query!=null && query.containsKey("flutter") && (Boolean) query.get("flutter")){
            //这里可以看到原生跳flutter页面,其实是跳转到FlutterWrapperActivity,而且会把query、params两个HashMap带过去
            Intent intent = new Intent(mAppContext,FlutterWrapperActivity.class);
            intent.setData(Uri.parse(url));
            intent.setAction(Intent.ACTION_VIEW);
            intent.putExtra("query", query);
            intent.putExtra("params", params);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mAppContext.startActivity(intent);
            return true;
        }
        ...
        return false;
    }
    

    再看FlutterWrapperActivity的onCreate方法

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.flutter_layout);
        checkIfAddFlutterView();
        fakeSnapImgView = (ImageView) findViewById(R.id.flutter_snap_imageview);
        fakeSnapImgView.setVisibility(View.GONE);
        //Process Intent Extra
        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        Uri uri = intent.getData();
        //会走这个if
        if (uri != null) {
            //把获取到的url、query、params传到HybridStackManager
            HybridStackManager.sharedInstance().openUrlFromFlutter(uri.toString(), (HashMap) intent.getSerializableExtra("query"), (HashMap) intent.getSerializableExtra("params"));
        } else if (bundle != null) {
            HybridStackManager.sharedInstance().openUrlFromFlutter(intent.getStringExtra("url"), (HashMap) intent.getSerializableExtra("query"), (HashMap) intent.getSerializableExtra("params"));
        }
        ...
    }
    

    HybridStackManager是个单例,然后我们看openUrlFromFlutter方法

    public void openUrlFromFlutter(String url, HashMap query, HashMap params) {
        //从这里我们看到参数是通过methodChannel传递给Flutter的,而assembleChanArgs方法会把url、query、parmas组装成一个新的HashMap
        HybridStackManager.sharedInstance().methodChannel.invokeMethod("openURLFromFlutter", assembleChanArgs(url, query, params));
    }
    

    接下来我们Flutter中的openURLFromFlutter方法,在Flutter端我们会先调用Router.sharedInstance(),该方法会调到setupMethodChannel方法,方法里边会设置MethodChannel的回调setMethodCallHandler

      void setupMethodChannel(){
        HybridStackManagerPlugin.hybridStackManagerPlugin
            .setMethodCallHandler((MethodCall methodCall)async{
          String method = methodCall.method;
          //这里我们看到了openURLFromFlutter方法,即原生会调到
          if (method == "openURLFromFlutter") {
            //获取组装后的Map
            Map args = methodCall.arguments;
            if (args != null) {
              bool animated = (args["animated"] == 1);
              //参数传到pushPageWithOptionsFromFlutter方法
              Router.sharedInstance().pushPageWithOptionsFromFlutter(
                  routeOption: new RouterOption(
                      url: args["url"],
                      query: args["query"],
                      params: args["params"]),
                  animated: animated ?? false);
            }
          } else if (method == "popToRoot") {
            Router.sharedInstance().popToRoot();
          } else if (method == "popToRouteNamed") {
            Router.sharedInstance().popToRouteNamed(methodCall.arguments);
          } else if (method == "popRouteNamed") {
            Router.sharedInstance().popRouteNamed(methodCall.arguments);
          }
          else if(method == "fetchSnapshot"){
             ...
          }
        });
      }
    

    我们看Router的pushPageWithOptionsFromFlutter方法

      pushPageWithOptionsFromFlutter({RouterOption routeOption, bool animated}) {
        //这个pageFromOption方法肯关键,它会返回一个Widget接下来会详细看这个方法
        Widget page =
            Router.sharedInstance().pageFromOption(routeOption: routeOption);
        if (page != null) {
          //会走这个if
          GlobalKey boundaryKey = new GlobalKey();
          //XMaterialPageRoute是MaterialPageRoute的子类
          XMaterialPageRoute pageRoute = new XMaterialPageRoute(
              settings: new RouteSettings(name: routeOption.userInfo),
              animated: animated,
              boundaryKey: boundaryKey,
              builder: (BuildContext context) {
                return new RepaintBoundary(key:boundaryKey,child: page);
              });
          //前边我们知道原生跳转flutter其实是跳转了一个FlutterWrapperActivity,然后把参数通过methodChannel传递给flutter,这里可以看出flutter这一端的页面栈其实也是通过Navigator管理的
          Navigator.of(globalKeyForRouter.currentContext).push(pageRoute);
          HybridStackManagerPlugin.hybridStackManagerPlugin
              .updateCurFlutterRoute(routeOption.userInfo);
        } else {
          HybridStackManagerPlugin.hybridStackManagerPlugin.openUrlFromNative(
              url: routeOption.url,
              query: routeOption.query,
              params: routeOption.params);
        }
        NavigatorState navState = Navigator.of(globalKeyForRouter.currentContext);
        List<Route<dynamic>> navHistory = navState.history;
      }
    

    接下来我们看Router的pageFromOption方法,先回忆下我们之前会给routerWidgetHandler赋值

    //给routerWidgetHandler赋值
    Router.sharedInstance().routerWidgetHandler =
        ({RouterOption routeOption, Key key}) {
      print(routeOption.url);
      print(routeOption.query);
      print(routeOption.params);
      switch (routeOption.url) {
        case "hrd://test":
          //返回一个Widget
          return MaterialApp(...);
      }
      return null;
    };
    
    typedef Widget FlutterWidgetHandler({RouterOption routeOption, Key key});
    //会给routerWidgetHandler赋值
    FlutterWidgetHandler routerWidgetHandler;
    
    pageFromOption({RouterOption routeOption, Key key}) {
      try {
        currentPageUrl = routeOption.url + "?" + converUrl(routeOption.query);
      } catch (e) {}
      routeOption.userInfo = Utils.generateUniquePageName(routeOption.url);
      if (routerWidgetHandler != null)
        //回调routerWidgetHandler方法并且返回Widget
        return routerWidgetHandler(routeOption: routeOption, key: key);
    }
    
    3、总结
    • 原生跳转flutter其实是跳转到FlutterWrapperActivity
    • 在FlutterWrapperActivity中参数会通过MethodChannel传递给flutter
    • 在flutter这一端的页面栈其实也是通过Navigator管理的

    二、Flutter跳原生

    1、使用

    Flutter

    HybridStackManagerPlugin.hybridStackManagerPlugin
        .openUrlFromNative(
            url: "hrd://test",
            query: {"title": "测试"});
    

    原生

    XURLRouter.sharedInstance().setNativeRouterHandler((url, query, params) -> {
        switch (url) {
            case "hrd://test":
                return TestActivity.class;
        }
        return null;
    });
    
    2、源码

    先看HybridStackManagerPlugin的openUrlFromNative方法

    openUrlFromNative({String url, Map query, Map params, bool animated}) {
      //会通过MethodChannel把参数传给原生
      _channel.invokeMethod("openUrlFromNative", {
        "url": url ?? "",
        "query": (query ?? {}),
        "params": (params ?? {}),
        "animated": animated ?? true
      });
    }
    

    接着看原生中的openUrlFromNative

    @Override
    public void onMethodCall(MethodCall call, Result result) {
        if (call.method.equals("openUrlFromNative")) {
            if (curFlutterActivity != null && curFlutterActivity.isActive()) {
                HashMap openUrlInfo = (HashMap)call.arguments;
                String url = (String)openUrlInfo.get("url");
                HashMap query = (HashMap)openUrlInfo.get("query");
                HashMap params = (HashMap)openUrlInfo.get("params");
                //将获取的参数组装成字符串
                String concatUrl = concatUrl(url, query, params);
                //调用FlutterWrapperActivity的openUrl方法
                curFlutterActivity.openUrl(concatUrl, params);
            }
            result.success("OK");
        } else if (call.method.equals("getMainEntryParams")) {
            ...
        }
    }
    

    我们看FlutterWrapperActivity的openUrl方法

    public void openUrl(String url, HashMap params) {
        HybridStackManager.sharedInstance().curFlutterActivity = null;
        if (url.contains("flutter=true")) {
            //flutter跳flutter需要设置flutter为true
            Intent intent = new Intent(FlutterWrapperActivity.this, FlutterWrapperActivity.class);
            intent.setAction(Intent.ACTION_RUN);
            intent.setData(Uri.parse(url));
            intent.putExtra("params", params);
            this.innerStartActivity(intent, true);
        } else {
            //会走这里
            Uri tmpUri = Uri.parse(url);
            String tmpUrl = String.format("%s://%s", tmpUri.getScheme(), tmpUri.getHost());
            HashMap query = new HashMap();
            for (String key : tmpUri.getQueryParameterNames()) {
                query.put(key, tmpUri.getQueryParameter(key));
            }
            //调XURLRouter的openUrlWithQueryAndParams方法
            XURLRouter.sharedInstance().openUrlWithQueryAndParams(tmpUrl, query, null);
            saveFinishSnapshot(false);
        }
    }
    
    public boolean openUrlWithQueryAndParams(String url, HashMap<String, Object> query, HashMap<String, Object> params){
        Uri tmpUri = Uri.parse(url);
        if(!kOpenUrlPrefix.equals(tmpUri.getScheme()))
            return false;
        if(query!=null && query.containsKey("flutter") && (Boolean) query.get("flutter")){
            //原生调会走这里,需要设置flutter为true
            Intent intent = new Intent(mAppContext,FlutterWrapperActivity.class);
            intent.setData(Uri.parse(url));
            intent.setAction(Intent.ACTION_VIEW);
            intent.putExtra("query", query);
            intent.putExtra("params", params);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mAppContext.startActivity(intent);
            return true;
        }
        if(mNativeRouterHandler!=null) {
            //这里会回调XURLRouterHandler的openUrlWithQueryAndParams方法并获取到一个Class,最后通过startActivity进行页面跳转
            Class activityCls =  mNativeRouterHandler.openUrlWithQueryAndParams(url, query, params);
            Intent intent = new Intent(mAppContext,activityCls);
            intent.setData(Uri.parse(url));
            intent.setAction(Intent.ACTION_VIEW);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            if (query != null) {
                for (String key : query.keySet()) {
                    intent.putExtra(key, String.valueOf(query.get(key)));
                }
            }
            if (params != null) {
                for (String key : params.keySet()) {
                    intent.putExtra(key, String.valueOf(params.get(key)));
                }
            }
            mAppContext.startActivity(intent);
        }
        return false;
    }
    

    我们在使用的时候会给上边的mNativeRouterHandler赋值

    public void setNativeRouterHandler(XURLRouterHandler handler){
        mNativeRouterHandler = handler;
    }
    
    XURLRouter.sharedInstance().setNativeRouterHandler((url, query, params) -> {
        switch (url) {
            case "hrd://test":
                return TestActivity.class;
        }
        return null;
    });
    
    3、总结
    • flutter跳原生其实是通过MethodChannel传值给原生
    • 原生这边接收到参数后会返回一个Class
    • 最终也是通过startActivity实现页面跳转

    三、参考

    相关文章

      网友评论

          本文标题:hybrid_stack_manager浅析

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