以下分析基于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
由于我们没有传入其他参数,因此urlParams和exts都是null。方法体中,通过新创建一个Map
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(参数设置);
参数需要自己设置。
上面的解决方案并不完善,只能暂时解决业务问题。如果看官有更好的处理方案,欢迎指教。