flutter_boost接入及分析

flutter_boost 地址:https://github.com/alibaba/flutter_boost
集成之后的项目地址:https://github.com/huangyuanlove/flutter_boost_demo
flutter版本: v1.5.4-hotfix.2
flutter_boost版本:0.1.52

集成过程

前提:项目已经集成flutter,并且可以运行

添加依赖

根据官方说法:
打开pubspec.yaml并将以下行添加到依赖项:

1
flutter_boost: ^0.1.52

或者可以直接依赖github的项目的版本,Tag,pub发布会有延迟,推荐直接依赖Github项目

1
2
3
4
5
6

flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '0.1.52'

然后 flutter packages get一下

在主工程的build.gradle中依赖一下
implementation project(path: ':flutter_boost'),官方没有提这个,但是我在项目中不添加这个依赖,找不到对应的类

在flutter_nodule侧

main.dart中注册一下路由,过程和使用命名路由相似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();

FlutterBoost.singleton.registerPageBuilders({
'first': (pageName, params, _) => FirstRouteWidget(),
'second': (pageName, params, _) => SecondRouteWidget(params),
'tab': (pageName, params, _) => TabRouteWidget(),
'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params),

///可以在native层通过 getContainerParams 来传递参数
'flutterPage': (pageName, params, _) {
print("flutterPage params:$params");

return FlutterRouteWidget();
},
});
}

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Boost example',
builder: FlutterBoost.init(postPush: _onRoutePushed),
home: Container());
}

void _onRoutePushed(
String pageName, String uniqueId, Map params, Route route, Future _) {
}

其中 FirstRouteWidgetSecondRouteWidgetTabRouteWidgetFragmentRouteWidgetFlutterRouteWidget代码可以在simple_page_widgets.dart中找到

在原生Android侧

修改Application继承FlutterApplication,并且在onCreate初始化FlutterBoost

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
FlutterBoost.init(new Platform() {

@Override
public Application getApplication() {
return MyApplication.this;
}

@Override
public boolean isDebug() {
return true;
}

@Override
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
//在flutter中调用FlutterBoost.singleton.open()方法,最终会走到这里进行处理
PageRouter.openPageByUrl(context,url,urlParams,requestCode);
}

@Override
public IFlutterEngineProvider engineProvider() {
return new BoostEngineProvider(){
@Override
public BoostFlutterEngine createEngine(Context context) {
return new BoostFlutterEngine(context, new DartExecutor.DartEntrypoint(
context.getResources().getAssets(),
FlutterMain.findAppBundlePath(context),
"main"),"/");
}
};
}

@Override
public int whenEngineStart() {
return ANY_ACTIVITY_CREATED;
}

@Override
public void registerPlugins(PluginRegistry registry) {
super.registerPlugins(registry);
}
});

Android侧包含flutter_view的容器,比如Activity需要继承BoostFlutterActivity,并实现getContainerUrl()getContainerUrlParams()方法
其中getContainerUrl方法返回的值就是在main.dart中注册的路由,getContainerUrlParams方法返回值则是对应的params

尝试

目前的做法是在Android这边配置打开的协议,和iOS统一,打开页面之后传递的参数全部附加在Uri上,
比如在flutter中的调用打开另外一个flutter界面,第一个参数uri就是sample://flutter/launch,如果是打开native页面,则是sample://native/launch
这样两端各自处理各自的逻辑就好了。
在Android端, PageRouter类用来处理native和flutter页面互相打开的逻辑,将传递进来的参数拼接成Uri的形式,通过Intent.setData()方式传递到下一个页面,而不用关系是打开native还是flutter,
在FlutterView的容器FlutterPageActivity中,我们通过解析Uri,还原一下要打开的flutter页面的router以及需要的参数,通过上面说的两个方法传递给flutter。

flutter_boost流程

在flutter中打开flutter或者原生
1
2
3
FlutterBoost.singleton.open("sample://nativePage", urlParams:{
"query": {"aaa": "bbb"}
})
1
2
3
4
5
6
7
8
9
Future<Map<String,dynamic>> open(String url,{Map<String,dynamic> urlParams,Map<String,dynamic> exts}){

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

这里也是通过channel调用原生的方法,方法名字是openPage

在FlutterBoost.BoostMethodHandler中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;

这里的mManager是FlutterViewContainerManager一个实例,openContainer方法的实现是这样的

1
2
3
4
void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) {
。。。
FlutterBoost.singleton().platform().openContainer(context,url,urlParams,requestCode,exts);
}

注意最后一句FlutterBoost.singleton().platform()这里的platform返回的是 IPlatform类型,也就是我们在Application中初始化FlutterBoost时传入的对象

1
FlutterBoost.init(new Platform(){...})

最终调用了这里面的openContainer方法,将路由处理交给了原生。

总结:

在flutter中调用 FlutterBoost.singleton.open 方法,通过channel调用原生,这里的channel的实现类是BoostChannel,在其初始化时注册的methodHandler。 调用原生FlutterBoost.BoostMethodHandler中的onMethodCall()中的case 'openPage',然后mManager.openContainer,之后就是 FlutterBoost.singleton().platform().openContainer(context,url,urlParams,requestCode,exts);

在flutter中关闭页面

调用

1
2
BoostContainerSettings settings = BoostContainer.of(context).settings;
FlutterBoost.singleton.close(settings.uniqueId,result: {"result":"data from second"});

在上面的flutter打开原生或者flutter页面时传的map参数,打下断点或者输出一下,可以看到里面会有一个__container_uniqueid_key__,
这个key是flutter_boost用来标示flutterview的容器,当我们需要在flutter中关闭页面的时候,需要传入这个key。
而关闭页面的时候

1
2
3
4
5
6
7
8
9
10
11
12
13
case "closePage":
{
try {
String uniqueId = methodCall.argument("uniqueId");
Map<String,Object> resultData = methodCall.argument("result");
Map<String,Object> exts = methodCall.argument("exts");

mManager.closeContainer(uniqueId, resultData,exts);
result.success(true);
}catch (Throwable t){
result.error("close page error",t.getMessage(),t);
}
}

也是通过FlutterViewContainerManager的实例进行关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
IContainerRecord closeContainer(String uniqueId, Map<String, Object> result,Map<String,Object> exts) {
IContainerRecord targetRecord = null;
for (Map.Entry<IFlutterViewContainer, IContainerRecord> entry : mRecordMap.entrySet()) {
if (TextUtils.equals(uniqueId, entry.getValue().uniqueId())) {
targetRecord = entry.getValue();
break;
}
}

if(targetRecord == null) {
Debuger.exception("closeContainer can not find uniqueId:" + uniqueId);
}

FlutterBoost.singleton().platform().closeContainer(targetRecord,result,exts);
return targetRecord;
}

调用FlutterBoost.singleton().platform()的关闭容器方法

1
2
3
4
5
6
@Override
public void closeContainer(IContainerRecord record, Map<String, Object> result, Map<String, Object> exts) {
if(record == null) return;

record.getContainer().finishContainer(result);
}

最终还是调用的record中container的finish方法。
而在ContainerRecord类(IContainerRecord的实现类)的onAppear方法中,会将当前页面放入自己维护的栈中。
具体执行顺序:
当打开FlutterView的容器(这里是我们自己继承自BoostFlutterActivity的类,实现了finishContainer()方法),在BoostFlutterActivity.onCreate()中,调用
FlutterBoost.singleton().containerManager().generateSyncer(this);,将当前的容器放在FlutterViewContainerManager中,并且记录上面讲到唯一标示
当关闭容器的时候,则从里面查找到对应的容器,执行finishContainer()。


flutter_boost接入及分析
https://blog.huangyuanlove.com/2019/09/12/flutter-boost接入及分析/
作者
HuangYuan_xuan
发布于
2019年9月12日
许可协议
BY HUANG兄