hybrid_stack_manager浅析

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

一、原生跳Flutter

1、使用

原生

HashMap m = new HashMap<>();
m.put("flutter", true);
HashMap 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 query, HashMap 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> 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 query, HashMap 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

你可能感兴趣的:(hybrid_stack_manager浅析)