項目中在使用FlutterBoost,對其實現(xiàn)原理比較好奇烛亦,看了一下,關(guān)鍵的類有點多,自己捋一捋,記錄一下吠谢,便于理解
FlutterBoost解決的混合開發(fā)過程中的幾個痛點:
- 統(tǒng)一了native和flutter之間跳轉(zhuǎn)方式
- 提供與native一致的生命周期管理
- 優(yōu)化FlutterEngine的使用,減少內(nèi)存消耗
- 其他(比如黑屏閃屏的坑)
關(guān)系詳解:todo
主要的結(jié)構(gòu)圖
關(guān)鍵類的作用 todo
關(guān)鍵類的層級關(guān)系
flutter跳轉(zhuǎn)native過程比較簡單清晰尿招,下面分析下flutter跳轉(zhuǎn)flutter的整個流程
跳轉(zhuǎn)入口
FlutterBoost.singleton.open(RouterConstants.USER_INFO_PAGE);
看一下open做了什么
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);
}
調(diào)用了BoostChannel(點進(jìn)去發(fā)現(xiàn)是對MethodChannel的一個包裝)的invokeMethod方法啊易,最終是調(diào)用的MethodChannel的invokeMethod,指向的’openPage‘
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
assert(method != "__event__");
return _methodChannel.invokeMethod<T>(method, arguments);
}
找一下native那邊對應(yīng)的注冊位置割去,F(xiàn)lutterBoostPlugin$BoostMethodHandler
class BoostMethodHandler implements MethodChannel.MethodCallHandler {
@Override
public void onMethodCall(MethodCall methodCall, final MethodChannel.Result result) {
FlutterViewContainerManager mManager = (FlutterViewContainerManager) FlutterBoost.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(), Log.getStackTraceString(t));
}
}
break;
...
default: {
result.notImplemented();
}
}
}
}
來到FlutterViewContainerManager里的OpenContainer方法
void openContainer(String url, Map<String, Object> urlParams, Map<String, Object> exts,OnResult onResult) {
...
FlutterBoost.instance().platform().openContainer(context,url,urlParams,requestCode,exts);
}
調(diào)用的是Platform類的openContainer,其實現(xiàn)在Flutterboost的build方法中
public Platform build() {
Platform platform = new Platform() {
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
router.openContainer(context, url, urlParams, requestCode, exts);
}
}
最終調(diào)用到router對象的OpenContainer,而這個router具體實現(xiàn),則在我們構(gòu)造Platform對象時生成的
INativeRouter router = (context, url, urlParams, requestCode, exts) -> {
String assembleUrl = Utils.assembleUrl(url, urlParams);
PageRouter.openPageByUrl(context, assembleUrl, urlParams);
};
繼續(xù)往下看,openPagerByUrl方法孤澎, 通過BoostFlutterActivity的一系列構(gòu)造,生成了一個intent
Intent intent = BoostFlutterActivity
.withNewEngine()
.url(pageName.get(path))
.params(params)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque)
.build(context);
而這個intent指向的目標(biāo)Activity其實就是BoostFlutterActivity,在調(diào)用withNewEngine時賦值
SerializableMap serializableMap = new SerializableMap();
serializableMap.setMap(params);
return new Intent(context, activityClass)
.putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
.putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false)
.putExtra(EXTRA_URL, url)
.putExtra(EXTRA_PARAMS, serializableMap);
public static NewEngineIntentBuilder withNewEngine() {
return new NewEngineIntentBuilder(BoostFlutterActivity.class);
}
ContainerCoordinator
接下來ContainerRecord中的MethodChannelProxy是關(guān)鍵
那么,intent里面配置的這些關(guān)鍵參數(shù)在哪里使用到了呢?看看FlutterActivityAndFragmentDeleg
ate里的onCreateView方法中調(diào)用的syncer關(guān)鍵類向拆,從名字和里面的內(nèi)容可以看得出,它的作用主要用來做生命周期同步的
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
...
mSyncer.onCreate();
return flutterSplashView;
}
具體實現(xiàn)在ContainerRecord的onCreate中,并且運用了一個代理模式,最終調(diào)用到了這一個方法
private void create() {
if (mState == STATE_UNKNOW) {
invokeChannelUnsafe("didInitPageContainer",
mContainer.getContainerUrl(),
mContainer.getContainerUrlParams(),
mUniqueId
);
//Debuger.log("didInitPageContainer");
mState = STATE_CREATED;
}
}
繼續(xù)往下深入幾層噪奄,可以看到注冊了一個名為“flutter_booost”的MethodChannel知允,將方法名和參數(shù)傳到了這里
mMethodChannel = new MethodChannel(registrar.messenger(), "flutter_boost");
那么在Dart端手负,是在哪里接收的呢蝠猬?我們查一下flutter里注冊的channel,發(fā)現(xiàn)果然存在一個BoostChannel
class BoostChannel {
final MethodChannel _methodChannel = MethodChannel("flutter_boost");
final Map<String, List<EventListener>> _eventListeners = Map();
final Set<MethodHandler> _methodHandlers = Set();
BoostChannel() {
_methodChannel.setMethodCallHandler((MethodCall call) {
if (call.method == "__event__") {
String name = call.arguments["name"];
Map arg = call.arguments["arguments"];
List<EventListener> list = _eventListeners[name];
if (list != null) {
for (EventListener l in list) {
l(name, arg);
}
}
} else {
for (MethodHandler handler in _methodHandlers) {
handler(call);
}
}
return Future<dynamic>.value();
});
}
}
由于方法名稱是didInitPageContainer堪夭,所以走的else分支嚣镜,再繼續(xù)往下走,可以看到另外一個關(guān)鍵類:ContainerCoordinator 棒搜,里面注冊了了很多方法回調(diào),包括我們要找的didInitPageContainer
Future<dynamic> _onMethodCall(MethodCall call) {
Logger.log("onMetohdCall ${call.method}");
switch (call.method) {
case "didInitPageContainer":
{
String pageName = call.arguments["pageName"];
Map params = call.arguments["params"];
String uniqueId = call.arguments["uniqueId"];
_nativeContainerDidInit(pageName, params, uniqueId);
}
break;
...
}
return Future<dynamic>(() {});
}
這里面有個關(guān)鍵方法_nativeContainerDidInit摸恍,
bool _nativeContainerDidInit(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.Init);
return true;
}
然后就在_pageBuilders里去找媚媒,有沒有name對應(yīng)的pageBuilder
BoostContainerSettings _createContainerSettings(
String name, Map params, String pageId) {
Widget page;
final BoostContainerSettings routeSettings = BoostContainerSettings(
uniqueId: pageId,
name: name,
params: params,
builder: (BuildContext ctx) {
//Try to build a page using keyed builder.
if (_pageBuilders[name] != null) {
page = _pageBuilders[name](name, params, pageId);
}
//Build a page using default builder.
if (page == null && _defaultPageBuilder != null) {
page = _defaultPageBuilder(name, params, pageId);
}
assert(page != null);
Logger.log('build widget:$page for page:$name($pageId)');
return page;
});
return routeSettings;
}
那么萄凤,這個pageBuilders又是什么噩死,何時賦值的呢?我們再回頭看看dart端flutter_boost路由注冊的地方
///Register a map builders
void registerPageBuilders(Map<String, PageBuilder> builders) {
ContainerCoordinator.singleton.registerPageBuilders(builders);
}
這個我們一般在dart的main.dart里初始化
FlutterBoost.singleton.registerPageBuilders({
RouterConstants.MINE_PAGE: (pageName, params, _) {
return MinePage(params);
},
RouterConstants.USER_INFO_PAGE: (pageName, params, _) {
return UserInfoPage(params);
}
});
這樣栈雳,flutter跳轉(zhuǎn)flutter頁面的主體流程就走完了痴奏,思路也更清晰了一些