abstract
This paper mainly focuses on how to run flutter on IOS, and summarizes the general running process.
The key classes involved are as follows:
- FlutterViewController
- FlutterView
- FlutterEngine
- DartIsolate
FlutterViewController
There must be a carrier for the embedded native application of the fluent. Starting from this point, the entry point of the API in the source code of the fluent engine isFlutterViewController
The source code of the header file is simplified as follows
@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
Constructor for the flutterviewcontroller
Flutterviewcontroller has two constructors, which are essentially the same. The first one is Google to create multiple constructorsFlutterViewController
In order to enable users to reuseFlutterEngine
And open.
- (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;
_engine.reset([engine retain]);
_engineNeedsLaunch = NO;
_flutterView.reset([[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);
_engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter"
project:project
allowHeadlessExecution:NO]);
_flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
[_engine.get() createShell:nil libraryURI:nil];
_engineNeedsLaunch = YES;
_ongoingTouches = [[NSMutableSet alloc] init];
[self loadDefaultSplashScreenView];
[self performCommonViewControllerInitialization];
}
return self;
}
In the constructor, we mainly do the following things:
- Initializes or replaces the current
FlutterEngine
- initialization
FlutterView
- Initializes the collection of gestures that are occurring
- Load flash page, pass in
FlutterEngine
The constructor does not have this item. It should be considered a lotFlutterViewController
It’s not good to load the flash screen page frequently - set up
UIInterfaceOrientationMask
andUIStatusBarStyle
- Add a series of notifications, including
Application
Life cycle, keyboard events,Accessibility
And so on - take
FlutterViewController
Set toFlutterEngine
This line of code is added to the second constructor, and the first one just delays the call
[_engine.get() createShell:nil libraryURI:nil];
Loadview of flutterviewcontroller
stayloadView
Function, set theFlutterViewController
Ofview
And determine whether the flash screen page needs to be loaded, which can be rewrittensplashScreenView
The get method ofnil
Do not load flash pages at all
- (void)loadView {
self.view = _flutterView.get();
self.view.multipleTouchEnabled = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self installSplashScreenViewIfNecessary];
}
Operation of navigator by fluterviewcontroller
FlutterViewController
Three interfaces are provided to allow us to access DART’sNavigator
Direct operation
- (void)setInitialRoute:(NSString*)route {
[[_engine.get() navigationChannel] invokeMethod:@"setInitialRoute" arguments:route];
}
- (void)popRoute {
[[_engine.get() navigationChannel] invokeMethod:@"popRoute" arguments:nil];
}
- (void)pushRoute:(NSString*)route {
[[_engine.get() navigationChannel] invokeMethod:@"pushRoute" arguments:route];
}
setInitialRoute
setInitialRoute
Through the IOS sidenavigationChannel
To tell dart the specific initialroute, this process is slightly special, and does not directly receive channel information at dart end,
It’s done at the engine level, web_ UI is not in the scope of this article’s analysis, here directly wash the points related to the original
setInitialRoute
The setting process is as follows:
DispatchPlatformMessage
-> HandleNavigationPlatformMessage
-> initial_route_
void Engine::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) {
if (message->channel() == kLifecycleChannel) {
if (HandleLifecyclePlatformMessage(message.get()))
return;
} else if (message->channel() == kLocalizationChannel) {
if (HandleLocalizationPlatformMessage(message.get()))
return;
} else if (message->channel() == kSettingsChannel) {
HandleSettingsPlatformMessage(message.get());
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;
document.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
In the endHandleNavigationPlatformMessage
Function is directly assigned to theinitial_route_
。
setInitialRoute
The reading process is as follows:
Window.defaultRouteName
-> DefaultRouteName
-> Engine::DefaultRouteName
-> initial_route_
As you can see, keywordnative
, which is a keyword added by dart to facilitate the binding of C / C + + export methods. The corresponding key isWindow_defaultRouteName
class Window {
String get defaultRouteName => _defaultRouteName();
String _defaultRouteName() native 'Window_defaultRouteName';
}
You can find the following function under the fluent namespace of the engine layer. The corresponding export function is registeredDefaultRouteName
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));
}
It’s down there engine.cc File under the function, readinitial_route_
Value of
std::string Engine::DefaultRouteName() {
if (!initial_route_.empty()) {
return initial_route_;
}
return "/";
}
So far, the process of setting the default routename on the native side and getting the value on the dart side is completed.
pushRoute and popRoute
The implementation method is mainly built-in through the enginenavigationChannel
Notify dart side, corresponding to dart sideSystemChannels
Class, there is a corresponding channel
static const MethodChannel navigation = MethodChannel(
'flutter/navigation',
JSONMethodCodec(),
);
Final treatmentpushRoute
andpopRoute
The logic ofWidgetsBinding
Class, mainly the following functions
Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
switch (methodCall.method) {
case 'popRoute':
return handlePopRoute();
case 'pushRoute':
return handlePushRoute(methodCall.arguments as String);
}
return Future<dynamic>.value();
}
Future<void> handlePushRoute(String route) async {
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
if (await observer.didPushRoute(route))
return;
}
}
Future<void> handlePopRoute() async {
for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
if (await observer.didPopRoute())
return;
}
SystemNavigator.pop();
}
This code indicates that only the called method returnstrue
The specific processing logic of each handle function is through aWidgetsBindingObserver
To achieve, continue to follow up to find the following code
class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
@override
Future<bool> didPopRoute() async {
assert(mounted);
final NavigatorState navigator = _navigator?.currentState;
if (navigator == null)
return false;
return await navigator.maybePop();
}
@override
Future<bool> didPushRoute(String route) async {
assert(mounted);
final NavigatorState navigator = _navigator?.currentState;
if (navigator == null)
return false;
navigator.pushNamed(route);
return true;
}
}
handlePopRoute
Function, if there is no oneobserver
returntrue
Is calledSystemNavigator.pop();
To exit the application
class SystemNavigator {
static Future<void> pop({bool animated}) async {
await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop', animated);
}
}
FlutterView
FlutterView
There are not too many functions, mainly two points:
- Pass in on initialization
FlutterViewEngineDelegate
- establish
flutter::IOSSurface
@protocol FlutterViewEngineDelegate <NSObject>
- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type
asBase64Encoded:(BOOL)base64Encode;
- (flutter::FlutterPlatformViewsController*)platformViewsController;
@end
@interface FlutterView : UIView
- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate
opaque:(BOOL)opaque NS_DESIGNATED_INITIALIZER;
- (std::unique_ptr<flutter::IOSSurface>)createSurface:
(std::shared_ptr<flutter::IOSGLContext>)context;
@end
takeScreenshot:asBase64Encoded:
Should beFlutterView
Data source for rendering, please refer todrawLayer:inContext:
Source code of
@implementation FlutterView
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
if (layer != self.layer || context == nullptr) {
return;
}
auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
asBase64Encoded:NO];
if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) {
return;
}
NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
length:screenshot.data->size()];
fml::CFRef<CGDataProviderRef> image_data_provider(
CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));
fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
fml::CFRef<CGImageRef> image(CGImageCreate(
screenshot.frame_size.width(), // size_t width
screenshot.frame_size.height(), // size_t height
8, // size_t bitsPerComponent
32, // size_t bitsPerPixel,
4 * screenshot.frame_size.width(), // size_t bytesPerRow
colorspace, // CGColorSpaceRef space
static_cast<CGBitmapInfo>(kCGImageAlphaPremultipliedLast |
kCGBitmapByteOrder32Big), // CGBitmapInfo bitmapInfo
image_data_provider, // CGDataProviderRef provider
nullptr, // const CGFloat* decode
false, // bool shouldInterpolate
kCGRenderingIntentDefault // CGColorRenderingIntent intent
));
const CGRect frame_rect =
CGRectMake(0.0, 0.0, screenshot.frame_size.width(), screenshot.frame_size.height());
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, CGBitmapContextGetHeight(context));
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, frame_rect, image);
CGContextRestoreGState(context);
}
@end
We’ll see that laterFlutterViewEngineDelegate
Actually, it wasFlutterEngine
Yes.
It’s not right hereIOSSurface
Too much parsing is based on three layers. You can choose which rendering method to use at compile time
- If it is a simulator, use the normal calayer
- In the case of metal rendering, cametallayer is used
- In the case of opengl rendering, caeagllayer is used
+ (Class)layerClass {
#if TARGET_IPHONE_SIMULATOR
return [CALayer class];
#else // TARGET_IPHONE_SIMULATOR
#if FLUTTER_SHELL_ENABLE_METAL
return [CAMetalLayer class];
#else // FLUTTER_SHELL_ENABLE_METAL
return [CAEAGLLayer class];
#endif // FLUTTER_SHELL_ENABLE_METAL
#endif // TARGET_IPHONE_SIMULATOR
}
staycreateSurface
Function is mainly to create three correspondingIOSSurface
CALayer -> IOSSurfaceSoftware
CAEAGLLayer -> IOSSurfaceGL
CAMetalLayer -> IOSSurfaceMetal
The next rendering is actually handed over toFlutterEngine
Self.
FlutterEngine
FlutterEngine
There are not many interfaces exposed to the outside world, but only a few
- Constructor,
initWithName:project:allowHeadlessExecution
,allowHeadlessExecutionAllow no strong dependency when initializing the engine
FlutterViewController` - Start the engine,
runWithEntrypoint:libraryURI:
Customizableentrypoint
- Releasing resources,
destroyContext
- Whether the semantic tree is established or not,
ensureSemanticsEnabled
There are few documents about semantic tree, which are probably needed in disabled mode -
FlutterViewController
Get / set - Finally, there’s a bunch of built-in channels
We are mainly concerned with the construction, start-up, release andFlutterViewController
That’s about it,FlutterTextureRegistry
, FlutterPluginRegistry
Not in the scope of this article
@interface FlutterEngine : NSObject <FlutterTextureRegistry, FlutterPluginRegistry>
- (instancetype)initWithName:(NSString*)labelPrefix
project:(nullable FlutterDartProject*)project
allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER;
- (BOOL)runWithEntrypoint:(nullable NSString*)entrypoint libraryURI:(nullable NSString*)uri;
- (void)destroyContext;
- (void)ensureSemanticsEnabled;
@property(nonatomic, weak) FlutterViewController* viewController;
@property(nonatomic, readonly, nullable) FlutterMethodChannel* localizationChannel;
@property(nonatomic, readonly) FlutterMethodChannel* navigationChannel;
@property(nonatomic, readonly) FlutterMethodChannel* platformChannel;
@property(nonatomic, readonly) FlutterMethodChannel* textInputChannel;
@property(nonatomic, readonly) FlutterBasicMessageChannel* lifecycleChannel;
@property(nonatomic, readonly) FlutterBasicMessageChannel* systemChannel;
@property(nonatomic, readonly) FlutterBasicMessageChannel* settingsChannel;
@property(nonatomic, readonly) NSObject<FlutterBinaryMessenger>* binaryMessenger;
@property(nonatomic, readonly, copy, nullable) NSString* isolateId;
@end
Construction of flutterengine
FlutterEngine
In the construction, we should pay attention to the following two points:
-
FlutterDartProject
initialization -
FlutterPlatformViewsController
Initialization of
- (instancetype)initWithName:(NSString*)labelPrefix
project:(FlutterDartProject*)project
allowHeadlessExecution:(BOOL)allowHeadlessExecution {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
NSAssert(labelPrefix, @"labelPrefix is required");
_allowHeadlessExecution = allowHeadlessExecution;
_labelPrefix = [labelPrefix copy];
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);
if (project == nil)
_dartProject.reset([[FlutterDartProject alloc] init]);
else
_dartProject.reset([project retain]);
_pluginPublications = [NSMutableDictionary new];
_platformViewsController.reset(new flutter::FlutterPlatformViewsController());
_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(onMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
return self;
}
Start up of flutterengine
FlutterEngine
The following categories should be paid attention to:
- FlutterDartProject
- flutter::ThreadHost
- flutter::Shell
- FlutterObservatoryPublisher
- FlutterPlatformViewsController
FlutterEngine
There are two main things to start
- createShell
- launchEngine
- (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
if ([self createShell:entrypoint libraryURI:libraryURI]) {
[self launchEngine:entrypoint libraryURI:libraryURI];
}
return _shell != nullptr;
}
createShell
createShell
The source code is more, and it is simplified as follows:
- initialization
MessageLoop
- initialization
ThreadHost
- set up
on_create_platform_view
Callback - set up
on_create_rasterizer
Callback - initialization
flutter::TaskRunners
, if onembedded_views_preview
The current thread’sTaskRunner
As a GPU threadTaskRunner
- establish
shell
Finally, isolate is started - establish
FlutterPlatformViewsController
- establish
FlutterObservatoryPublisher
- set up
PlatformView
channels
- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
// ……
fml::MessageLoop::EnsureInitializedForCurrentThread();
_threadHost = {threadLabel.UTF8String, flutter::ThreadHost::Type::UI |
flutter::ThreadHost::Type::GPU |
flutter::ThreadHost::Type::IO};
flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
[](flutter::Shell& shell) {
return std::make_unique<flutter::PlatformViewIOS>(shell, shell.GetTaskRunners());
};
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
[](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell, shell.GetTaskRunners());
};
if (flutter::IsIosEmbeddedViewsPreviewEnabled()) {
flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
fml::MessageLoop::GetCurrent().GetTaskRunner(), // gpu
_threadHost.ui_thread->GetTaskRunner(), // ui
_threadHost.io_thread->GetTaskRunner() // io
);
// Create the shell. This is a blocking operation.
_shell = flutter::Shell::Create(std::move(task_runners), // task runners
std::move(settings), // settings
on_create_platform_view, // platform view creation
on_create_rasterizer // rasterzier creation
);
} else {
flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
_threadHost.gpu_thread->GetTaskRunner(), // gpu
_threadHost.ui_thread->GetTaskRunner(), // ui
_threadHost.io_thread->GetTaskRunner() // io
);
// Create the shell. This is a blocking operation.
_shell = flutter::Shell::Create(std::move(task_runners), // task runners
std::move(settings), // settings
on_create_platform_view, // platform view creation
on_create_rasterizer // rasterzier creation
);
}
if (_shell != nullptr) {
[self setupChannels];
if (!_platformViewsController) {
_platformViewsController.reset(new flutter::FlutterPlatformViewsController());
}
_publisher.reset([[FlutterObservatoryPublisher alloc] init]);
[self maybeSetupPlatformViewChannels];
}
return _shell != nullptr;
}
Here you can see that four will be activatedTaskRunner
, which are platform, GPU, UI, IO, respectively, but they do not necessarily correspond to four threads.
launchEngine
launchEngine
Actually, it’s still thereshell
Operations on
- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil {
// Launch the Dart application with the inferred run configuration.
self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryOrNil]);
}
void Shell::RunEngine(RunConfiguration run_configuration) {
RunEngine(std::move(run_configuration), nullptr);
}
void Shell::RunEngine(RunConfiguration run_configuration,
std::function<void(Engine::RunStatus)> result_callback) {
auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(),
result_callback](Engine::RunStatus run_result) {
if (!result_callback) {
return;
}
platform_runner->PostTask(
[result_callback, run_result]() { result_callback(run_result); });
};
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
if (!weak_engine_) {
result(Engine::RunStatus::Failure);
}
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable(
[run_configuration = std::move(run_configuration),
weak_engine = weak_engine_, result]() mutable {
if (!weak_engine) {
FML_LOG(ERROR)
<< "Could not launch engine with configuration - no engine.";
result(Engine::RunStatus::Failure);
return;
}
auto run_result = weak_engine->Run(std::move(run_configuration));
if (run_result == flutter::Engine::RunStatus::Failure) {
FML_LOG(ERROR) << "Could not launch engine with configuration.";
}
result(run_result);
}));
}
If you follow it up, you will end up in [shell > common > engine.cc ]Insiderun
Function, the core is this line of codePrepareAndLaunchIsolate
In the end, the whole process runs down to start isolate
Engine::RunStatus Engine::Run(RunConfiguration configuration) {
if (!configuration.IsValid()) {
FML_LOG(ERROR) << "Engine run configuration was invalid.";
return RunStatus::Failure;
}
auto isolate_launch_status =
PrepareAndLaunchIsolate(std::move(configuration));
if (isolate_launch_status == Engine::RunStatus::Failure) {
FML_LOG(ERROR) << "Engine not prepare and launch isolate.";
return isolate_launch_status;
} else if (isolate_launch_status ==
Engine::RunStatus::FailureAlreadyRunning) {
return isolate_launch_status;
}
std::shared_ptr<DartIsolate> isolate =
runtime_controller_->GetRootIsolate().lock();
bool isolate_running =
isolate && isolate->GetPhase() == DartIsolate::Phase::Running;
if (isolate_running) {
tonic::DartState::Scope scope(isolate.get());
if (settings_.root_isolate_create_callback) {
settings_.root_isolate_create_callback();
}
if (settings_.root_isolate_shutdown_callback) {
isolate->AddIsolateShutdownCallback(
settings_.root_isolate_shutdown_callback);
}
std::string service_id = isolate->GetServiceId();
fml::RefPtr<PlatformMessage> service_id_message =
fml::MakeRefCounted<flutter::PlatformMessage>(
kIsolateChannel,
std::vector<uint8_t>(service_id.begin(), service_id.end()),
nullptr);
HandlePlatformMessage(service_id_message);
}
return isolate_running ? Engine::RunStatus::Success
: Engine::RunStatus::Failure;
}
DartIsolate
YesPrepareAndLaunchIsolate
The function is simplified, leaving two points
- PrepareIsolate
- RunFromLibrary
Engine::RunStatus Engine::PrepareAndLaunchIsolate(RunConfiguration configuration) {
// ……
if (!isolate_configuration->PrepareIsolate(*isolate)) {
return RunStatus::Failure;
}
if (!isolate->RunFromLibrary(configuration.GetEntrypointLibrary(),
configuration.GetEntrypoint(),
settings_.dart_entrypoint_args)) {
return RunStatus::Failure;
}
return RunStatus::Success;
}
Let’s take a look at itRunFromLibrary
What did you do
- Find entrypoint
- Call the function of entrypoint,
InvokeMainEntrypoint
bool DartIsolate::RunFromLibrary(const std::string& library_name,
const std::string& entrypoint_name,
const std::vector<std::string>& args,
fml::closure on_run) {
tonic::DartState::Scope scope(this);
auto user_entrypoint_function =
Dart_GetField(Dart_LookupLibrary(tonic::ToDart(library_name.c_str())),
tonic::ToDart(entrypoint_name.c_str()));
auto entrypoint_args = tonic::ToDart(args);
if (!InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) {
return false;
}
phase_ = Phase::Running;
if (on_run) {
on_run();
}
return true;
}
I want to see othersInvokeMainEntrypoint
What has been done? The source code has been simplified. We can find the corresponding function in dart end
_getStartMainIsolateFunction
_runMainZoned
static bool InvokeMainEntrypoint(Dart_Handle user_entrypoint_function,
Dart_Handle args) {
Dart_Handle start_main_isolate_function =
tonic::DartInvokeField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")),
"_getStartMainIsolateFunction", {});
if (tonic::LogIfError(tonic::DartInvokeField(
Dart_LookupLibrary(tonic::ToDart("dart:ui")), "_runMainZoned",
{start_main_isolate_function, user_entrypoint_function, args}))) {
FML_LOG(ERROR) << "Could not invoke the main entrypoint.";
return false;
}
return true;
}
The next step is the tonic library, which is the Library under fuchsia. Google does not open source it to GitHub, but you can see Google source here. Later, we will sort out the tonic Library in other articles.
summary
Flutter runs on IOS. From the source code level, it has the following achievements:
- It reuses the existing three types of calayer to draw the interface,
drawLayer
Is calledtakeScreenshot
To obtain the raster image of the fluent interface - The corresponding semantic tree will not be built on the native side, and additional generation is required
- Flutter itself will run in a completely independent thread environment. We need to pay attention to the following four aspects
TaskRunner
Platform taskrunner is not necessarily a separate thread - Platform taskrunner, all interaction between native end and fluent will be processed in platform taskrunner
- The dart end can pass through the
native
Keyword calls the C / C + + function to get the basic type of data return value, and the performance is better than channel -
FlutterViewController
Forward all gesture interactions related to them to the flutterengine
Flow chart of fluent
The flow of the whole flutter operation can be summarized as follows, mainly focusing on the engine side, and the dart process is not expanded for reference only
- Looking for dartlibrary
- Locate entrypoint
- establish
FlutterEngine
, pass in dartlibrary and entrypoint - establish
FlutterViewController
,FlutterView
- set up
FlutterEngine
OfviewController
- Create a shell and start dart VM
- Load dartlibrary and run DART’s entrypoint
- Intercept the interface of dart UI, rasterize it and draw calayer
Author’s other articles
How to introduce flutter into existing applications seamlessly?
Practice of flutter’s innovative business at B end of halo travel