Flutter引擎源码解读-Flutter是如何在iOS上运⾏起来的
⽂章⽬录
摘要
本⽂主要是针对 Flutter 在 iOS 上是如何运⾏起来的源码进⾏串联,总结⼤致的运⾏流程。
涉及到的关键类有以下⼏个:
FlutterViewController
FlutterView
FlutterEngine
DartIsolate
FlutterViewController
Flutter 嵌⼊原⽣应⽤必须有个载体,从这个点⼊⼿,在 Flutter Engine 源码中的 API 的⼊⼝点是 FlutterViewController,对其头⽂件源码做精简,⼤致如下
@interface FlutterViewController : UIViewController <FlutterTextureRegistry, FlutterPluginRegistry>
- (instancetype)initWithEngine:(FlutterEngine*)engine
nibName:(nullable NSString*)nibName
bundle:(nullable NSBundle*)nibBundle NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithProject:(nullable FlutterDartProject*)project
nibName:(nullable NSString*)nibName
bundle:(nullable NSBundle*)nibBundle NS_DESIGNATED_INITIALIZER;
- (void)handleStatusBarTouches:(UIEvent*)event;
- (void)setFlutterViewDidRenderCallback:(void (^)(void))callback;
- (NSString*)lookupKeyForAsset:(NSString*)asset;
- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package;
-
(void)setInitialRoute:(NSString*)route;
- (void)popRoute;
- (void)pushRoute:(NSString*)route;
- (id<FlutterPluginRegistry>)pluginRegistry;
@property(nonatomic, readonly, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;
@property(strong, nonatomic) UIView* splashScreenView;
- (BOOL)loadDefaultSplashScreenView;
@property(nonatomic, getter=isViewOpaque) BOOL viewOpaque;
@property(weak, nonatomic, readonly) FlutterEngine* engine;
@property(nonatomic, readonly) NSObject<FlutterBinaryMessenger>* binaryMessenger;
@end
FlutterViewController 的构造函数
FlutterViewController 有两个构造函数,本质上是⼀样的,第⼀个构造函数是⾕歌为了在存在多个 FlutterViewController 的场景下为了让使⽤者能复⽤ FlutterEngine ⽽开放的。
- (instancetype)initWithEngine:(FlutterEngine*)engine
nibName:(nullable NSString*)nibName
bundle:(nullable NSBundle*)nibBundle {
NSAssert(engine != nil, @"Engine is required");
self = [super initWithNibName:nibName bundle:nibBundle];
if (self) {
_viewOpaque = YES;
_set([engine retain]);
_engineNeedsLaunch = NO;
_set([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
_ongoingTouches = [[NSMutableSet alloc] init];
[self performCommonViewControllerInitialization];
[engine setViewController:self];
}
return self;
}
- (instancetype)initWithProject:(nullable FlutterDartProject*)project
nibName:(nullable NSString*)nibName
bundle:(nullable NSBundle*)nibBundle {
self = [super initWithNibName:nibName bundle:nibBundle];
if (self) {
_viewOpaque = YES;
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
_set([[FlutterEngine alloc] initWithName:@"io.flutter"
project:project
allowHeadlessExecution:NO]);
_set([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
[_() createShell:nil libraryURI:nil];
_engineNeedsLaunch = YES;
_ongoingTouches = [[NSMutableSet alloc] init];
[self loadDefaultSplashScreenView];
[self performCommonViewControllerInitialization];
}
return self;
}
在构造函数中主要做了这么⼏件事情:
初始化或者替换当前的 FlutterEngine
初始化 FlutterView
初始化正在发⽣的⼿势集合
加载闪屏页,传⼊ FlutterEngine 的构造函数没有这项,应该是考虑了多 FlutterViewController 的场景下不好频繁加载闪屏页设置 UIInterfaceOrientationMask 和 UIStatusBarStyle
添加⼀系列的通知,包括 Application 的⽣命周期,键盘事件,Accessibility的事件等
将 FlutterViewController 设置给 FlutterEngine
第⼆个构造函数中还多了这⾏代码,第⼀个构造函数把这个调⽤延后了⽽已
[_() createShell:nil libraryURI:nil];
FlutterViewController 的 loadView
在 loadView 函数中,设置了 FlutterViewController 的 view,并判断是否需要加载闪屏页,可以通过重写 splashScreenView 的 get ⽅法返回nil 的⽅式彻底不加载闪屏页
- (void)loadView {
self.view = _();
self.view.multipleTouchEnabled = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self installSplashScreenViewIfNecessary];
}
FlutterViewController 对 Navigator 的操作
FlutterViewController 提供了三个接⼝允许我们在原⽣端对 dart 的 Navigator 直接进⾏操作
- (void)setInitialRoute:(NSString*)route {
[[_() navigationChannel] invokeMethod:@"setInitialRoute" arguments:route];
}
- (void)popRoute {
[[_() navigationChannel] invokeMethod:@"popRoute" arguments:nil];
}
- (void)pushRoute:(NSString*)route {
[[_() navigationChannel] invokeMethod:@"pushRoute" arguments:route];
}
setInitialRoute
setInitialRoute 在 iOS 端通过 navigationChannel 来告诉 dart 具体的 initialRoute,这个过程略微特殊,并不会在 dart 端直接接收channel 信息,
⽽是在引擎层⾯做了处理,web_ui 不在本⽂的解析范畴,这⾥直接洗跟原⽣相关的点
setInitialRoute 设置流程如下:
DispatchPlatformMessage -> HandleNavigationPlatformMessage -> initial_route_
void Engine::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) {
if (message->channel() == kLifecycleChannel) {
if (()))
return;
} else if (message->channel() == kLocalizationChannel) {
if (()))
return;
} else if (message->channel() == kSettingsChannel) {
());
return;
}
if (runtime_controller_->IsRootIsolateRunning() &&
runtime_controller_->DispatchPlatformMessage(std::move(message))) {
return;
}
// If there's no runtime_, we may still need to set the initial route.
if (message->channel() == kNavigationChannel) {
HandleNavigationPlatformMessage(std::move(message));
return;
}
FML_DLOG(WARNING) << "Dropping platform message on channel: "
<< message->channel();
}
bool Engine::HandleNavigationPlatformMessage(
fml::RefPtr<PlatformMessage> message) {
const auto& data = message->data();
rapidjson::Document document;
flutter开发appdocument.Parse(reinterpret_cast<const char*>(data.data()), data.size());
if (document.HasParseError() || !document.IsObject())
return false;
auto root = document.GetObject();
auto method = root.FindMember("method");
if (method->value != "setInitialRoute")
return false;
auto route = root.FindMember("args");
initial_route_ = std::move(route->value.GetString());
return true;
}
setInitialRoute 最终在 HandleNavigationPlatformMessage 函数中直接被赋值给 initial_route_。
setInitialRoute 读取流程如下:
Window.defaultRouteName -> DefaultRouteName -> Engine::DefaultRouteName -> initial_route_
可以看到,关键字 native,这是 dart 为了⽅便绑定 C/C++ 导出⽅法⽽添加的关键字,对应的 key 是 Window_defaultRouteName
class Window {
String get defaultRouteName => _defaultRouteName();
String _defaultRouteName() native 'Window_defaultRouteName';
}
可以到在引擎层的 flutter 命名空间下,有下⾯这个函数,注册了对应的导出函数,这⾥对应的是 DefaultRouteName
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
{"Window_setNeedsReportTimings", SetNeedsReportTimings, 2, true},
});
}
void DefaultRouteName(Dart_NativeArguments args) {
std::string routeName =
UIDartState::Current()->window()->client()->DefaultRouteName();
Dart_SetReturnValue(args, tonic::StdStringToDart(routeName));
}
再往下就是到 ⽂件下⾯的函数,读取 initial_route_ 的值
std::string Engine::DefaultRouteName() {
if (!initial_route_.empty()) {
return initial_route_;
}
return "/";
}
⾄此,完成了在原⽣端设置 defaultRouteName,在 dart 端获取该值的流程。
pushRoute and popRoute

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。