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

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

channel.invokeMethod<Map<dynamic,dynamic>>('openPage', properties);

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

class BoostChannel{
  final MethodChannel _methodChannel = MethodChannel("flutter_boost");
  ………
  Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
    assert(method != "__event__");
    return _methodChannel.invokeMethod<T>(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<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(), t);
                    }
                }
                break;
               ……
                default: {
                    result.notImplemented();
                }
            }
        }
    }

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

mManager.openContainer(url, params, exts, new FlutterViewContainerManager.OnResult() {
    @Override
    public void onResult(Map<String, Object> 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<String,Object> 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<String,Object> 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<String, Object> rlt) {
                                if (result != null) {
                                    result.success(rlt);
                                }
                            }
                        });

因此代码如下

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

mManager.setContainerResult(参数设置);

参数需要自己设置。

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

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

推荐阅读更多精彩内容