flutter_boost中关于Flutter页面打开原生页面并取得返回结果的分析

以下分析基于flutter_boost分支feature/flutter_1.9_androidx_upgrade

在Android当activityA打开activityB之后,如果想要在activityA中拿到activityB的结果,一般是通过onActivityResutl()方法获得。

这在Flutter当中相当于Future。原生flutter中取得这个future很简单。因为从flutterA跳转到flutterB,其实还是在一个activity中实现的,只需要

      var future = Navigator.of(context).pushNamed("destRouteName");

就可以拿到这个future。


但是如果用到了混合栈,比如flutterA-->activityB这样的话,在flutterA中想要取得activityB的返回结果,就相对麻烦了。这里提供的是解决flutter_boost中出现此类问题的方案。

一 分析

1:Flutter侧

在flutter_boost中,如果在flutterA中想要打开flutterB页面。在flutterA中可以调用

var future =await FlutterBoost.singleton.open("flutterB");
future.then((result){
  print($result);
});

此时如果是默认情况下,那么当flutterB页面关闭时,将会在flutterA中打印

{_resultCode__: -1, _requestCode__: -1}

当我们在flutterA中调用了FlutterBoost.singleton.open("flutterB")实际上是进入了

  Future> open(String url,{Map urlParams,Map exts}){

    Map properties = new Map();
    properties["url"] = url;
    properties["urlParams"] = urlParams;
    properties["exts"] = exts;
    return channel.invokeMethod>(
        'openPage', properties);
  }

由于我们没有传入其他参数,因此urlParamsexts都是null。方法体中,通过新创建一个Map properties将所有参数组装进来。然后进入

channel.invokeMethod>('openPage', properties);

这个channel是flutter_boost库中的BoostChannel。而BoostChannel其实内部维护了一个MethodChannel

class BoostChannel{
  final MethodChannel _methodChannel = MethodChannel("flutter_boost");
  ………
  Future invokeMethod(String method, [ dynamic arguments ]) async {
    assert(method != "__event__");
    return _methodChannel.invokeMethod(method,arguments);
  }
}

2:Android侧

上面在flutterA中打开一个flutterB页面,通过调用flutter_boost内部的open方法实现。该open方法实际上是通过methodChannel来调用Android测的方法来最终打开flutterB页面的。其实在flutter_boost中,任何一个flutter页面都有一个NewBoostFlutterActivity与之对应。当调用找到了"openPage"方法时

 class BoostMethodHandler implements MethodChannel.MethodCallHandler {

        @Override
        public void onMethodCall(MethodCall methodCall, final MethodChannel.Result result) {

            FlutterViewContainerManager mManager = (FlutterViewContainerManager) NewFlutterBoost.instance().containerManager();
            switch (methodCall.method) {
                ……
                case "openPage": {
                    try {
                        Map params = methodCall.argument("urlParams");
                        Map exts = methodCall.argument("exts");
                        String url = methodCall.argument("url");

                        mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
                            @Override
                            public void onResult(Map rlt) {
                                if (result != null) {
                                    result.success(rlt);
                                }
                            }
                        });
                    } catch (Throwable t) {
                        result.error("open page error", t.getMessage(), t);
                    }
                }
                break;
               ……
                default: {
                    result.notImplemented();
                }
            }
        }
    }

在openPage中取出参数。然后调用

mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
    @Override
    public void onResult(Map rlt) {
        if (result != null) {
            result.success(rlt);
        }
    }
});

而刚才我们在关闭flutterB页面时,flutterA页面中打印了

{_resultCode__: -1, _requestCode__: -1}

这个打印结果就是通过

result.success(rlt);

返回的。而这个onResult()方法是在FlutterViewContainerManager中调用的

void setContainerResult(IContainerRecord record,int requestCode, int resultCode, Map result) {
    IFlutterViewContainer target = findContainerById(record.uniqueId());
    if(target == null) {
        Debuger.exception("setContainerResult error, url="+record.getContainer().getContainerUrl());
    }
    if (result == null) {
        result = new HashMap<>();
    }
    result.put("_requestCode__",requestCode);
    result.put("_resultCode__",resultCode);
    final OnResult onResult = mOnResults.remove(record.uniqueId());
    if(onResult != null) {
        onResult.onResult(result);
    }
}

而setContainerResult()在两个地方进行了调用。

(1) 在onDestory()
 NewBoostFlutterAcitivity.onDestory();
    delegate.onDestroyView(); 
                mSyncer.onDestroy(); //IOperateSyncer的具体实现是ContainerRecord

在这个具体显示中返回了-1

 @Override
public void onDestroy() {
    Utils.assertCallOnMainThread();
    if (mState != STATE_DISAPPEAR) {
        Debuger.exception("state error");
    }
    mState = STATE_DESTROYED;
    mProxy.destroy();
    mManager.removeRecord(this);
    mManager.setContainerResult(this,-1,-1,null);
}
(2)在onActivityResult()
NewBoostFlutterAcitivity.onActivityResult()
            delegate.onActivityResult(requestCode, resultCode, data);

在delegate中

void onActivityResult(int requestCode, int resultCode, Intent data) {
    mSyncer.onActivityResult(requestCode,resultCode,data);
    ……
    mSyncer.onContainerResult(requestCode,resultCode,result);
    ……
}

二:结论

在回顾一下问题,如果我们要在flutterA页面中打开B页面,并且想要获得B页面的返回结果。需要区别B页面是Flutter页面还是Android原生页面。

1:B页面是Flutter页面

那就要根据实际情况重新在onDestory中返回不同的参数,而不是一刀切的返回-1,-1,null

setContainerResult(IContainerRecord record,int requestCode, int resultCode, Map result)
2:B页面是原生页面

由于methodChannel中的MethodChannel.Result对象只有在FlutterBoostPlugin中的OnResult中的onResult()中可以调用。

            FlutterViewContainerManager mManager = (FlutterViewContainerManager) NewFlutterBoost.instance().containerManager();

 mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
                            @Override
                            public void onResult(Map rlt) {
                                if (result != null) {
                                    result.success(rlt);
                                }
                            }
                        });

因此代码如下

 FlutterViewContainerManager mManager = (FlutterViewContainerManager) NewFlutterBoost.instance().containerManager();

mManager.setContainerResult(参数设置);

参数需要自己设置。

上面的解决方案并不完善,只能暂时解决业务问题。如果看官有更好的处理方案,欢迎指教。

你可能感兴趣的:(flutter_boost中关于Flutter页面打开原生页面并取得返回结果的分析)