From 922f141885c0f5d1797cebaace421018e2c843ce Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Wed, 9 Dec 2020 16:22:40 -0800 Subject: [PATCH 1/8] Implemented FlutterEngineGroup and Spawn API. --- ci/licenses_golden/licenses_flutter | 3 + shell/common/shell.cc | 15 +++ shell/common/shell.h | 13 ++ shell/common/shell_unittests.cc | 119 ++++++++++++++++++ shell/platform/darwin/ios/BUILD.gn | 3 + .../darwin/ios/framework/Headers/Flutter.h | 1 + .../framework/Headers/FlutterEngineGroup.h | 25 ++++ .../ios/framework/Source/FlutterEngine.mm | 66 ++++++++-- .../framework/Source/FlutterEngineGroup.mm | 60 +++++++++ .../Source/FlutterEngineGroupTest.mm | 41 ++++++ .../ios/framework/Source/FlutterEngineTest.mm | 24 ++++ .../framework/Source/FlutterEngine_Internal.h | 11 ++ .../ios/framework/Source/FlutterEngine_Test.h | 3 + 13 files changed, 372 insertions(+), 12 deletions(-) create mode 100644 shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h create mode 100644 shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm create mode 100644 shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e87e78e4dbadd..745ed885b1e65 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -945,6 +945,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDel FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -963,6 +964,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartPro FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 5ec02060f5b7b..a8fa087317c3c 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -436,6 +436,21 @@ Shell::~Shell() { platform_latch.Wait(); } +std::unique_ptr Shell::Spawn( + Settings settings, + const CreateCallback& on_create_platform_view, + const CreateCallback& on_create_rasterizer) { + RunConfiguration configuration = + RunConfiguration::InferFromSettings(settings); + TaskRunners task_runners = task_runners_; + FML_DCHECK(task_runners.IsValid()); + std::unique_ptr result(Shell::Create(std::move(task_runners), settings, + on_create_platform_view, + on_create_rasterizer)); + result->RunEngine(std::move(configuration)); + return result; +} + void Shell::NotifyLowMemoryWarning() const { auto trace_id = fml::tracing::TraceNonce(); TRACE_EVENT_ASYNC_BEGIN0("flutter", "Shell::NotifyLowMemoryWarning", diff --git a/shell/common/shell.h b/shell/common/shell.h index 6ff3a0407bb24..0c12e5e4c8613 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -220,6 +220,19 @@ class Shell final : public PlatformView::Delegate, /// ~Shell(); + //---------------------------------------------------------------------------- + /// @brief Creates one Shell from another Shell where the created Shell + /// takes the opportunity share any internal components it can. + /// This results is a Shell that has a smaller startup time cost + /// and a smaller memory footprint than an Shell created with a + /// Create function. + /// + /// @see http://flutter.dev/go/multiple-engines + std::unique_ptr Spawn( + Settings settings, + const CreateCallback& on_create_platform_view, + const CreateCallback& on_create_rasterizer); + //---------------------------------------------------------------------------- /// @brief Starts an isolate for the given RunConfiguration. /// diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 9fef249047449..c3b0f13c1be2d 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -32,6 +32,7 @@ #include "flutter/shell/common/vsync_waiter_fallback.h" #include "flutter/shell/version/version.h" #include "flutter/testing/testing.h" +#include "gmock/gmock.h" #include "third_party/rapidjson/include/rapidjson/writer.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/tonic/converter/dart_converter.h" @@ -42,6 +43,77 @@ namespace flutter { namespace testing { +namespace { +class MockPlatformViewDelegate : public PlatformView::Delegate { + MOCK_METHOD1(OnPlatformViewCreated, void(std::unique_ptr surface)); + + MOCK_METHOD0(OnPlatformViewDestroyed, void()); + + MOCK_METHOD1(OnPlatformViewSetNextFrameCallback, + void(const fml::closure& closure)); + + MOCK_METHOD1(OnPlatformViewSetViewportMetrics, + void(const ViewportMetrics& metrics)); + + MOCK_METHOD1(OnPlatformViewDispatchPlatformMessage, + void(fml::RefPtr message)); + + MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, + void(std::unique_ptr packet)); + + MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction, + void(int32_t id, + SemanticsAction action, + std::vector args)); + + MOCK_METHOD1(OnPlatformViewSetSemanticsEnabled, void(bool enabled)); + + MOCK_METHOD1(OnPlatformViewSetAccessibilityFeatures, void(int32_t flags)); + + MOCK_METHOD1(OnPlatformViewRegisterTexture, + void(std::shared_ptr texture)); + + MOCK_METHOD1(OnPlatformViewUnregisterTexture, void(int64_t texture_id)); + + MOCK_METHOD1(OnPlatformViewMarkTextureFrameAvailable, + void(int64_t texture_id)); + + MOCK_METHOD3(LoadDartDeferredLibrary, + void(intptr_t loading_unit_id, + std::unique_ptr snapshot_data, + std::unique_ptr snapshot_instructions)); + + MOCK_METHOD3(LoadDartDeferredLibraryError, + void(intptr_t loading_unit_id, + const std::string error_message, + bool transient)); + + MOCK_METHOD1(UpdateAssetManager, + void(std::shared_ptr asset_manager)); +}; + +class MockSurface : public Surface { + MOCK_METHOD0(IsValid, bool()); + + MOCK_METHOD1(AcquireFrame, + std::unique_ptr(const SkISize& size)); + + MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix()); + + MOCK_METHOD0(GetContext, GrDirectContext*()); + + MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr()); + + MOCK_METHOD0(ClearRenderContext, bool()); +}; + +class MockPlatformView : public PlatformView { + public: + MockPlatformView(MockPlatformViewDelegate& delegate, TaskRunners task_runners) + : PlatformView(delegate, task_runners) {} + MOCK_METHOD0(CreateRenderingSurface, std::unique_ptr()); +}; +} // namespace static bool ValidateShell(Shell* shell) { if (!shell) { @@ -2318,5 +2390,52 @@ TEST_F(ShellTest, AssetManagerMulti) { } } +TEST_F(ShellTest, Spawn) { + auto settings = CreateSettingsForFixture(); + auto shell = CreateShell(settings); + ASSERT_TRUE(ValidateShell(shell.get())); + + auto configuration = RunConfiguration::InferFromSettings(settings); + ASSERT_TRUE(configuration.IsValid()); + configuration.SetEntrypoint("fixturesAreFunctionalMain"); + + fml::AutoResetWaitableEvent main_latch; + AddNativeCallback( + "SayHiFromFixturesAreFunctionalMain", + CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); })); + + RunEngine(shell.get(), std::move(configuration)); + main_latch.Wait(); + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + + { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), + [this, &spawner = shell, &latch, settings]() { + MockPlatformViewDelegate platform_view_delegate; + auto spawn = spawner->Spawn( + settings, + [&platform_view_delegate](Shell& shell) { + auto result = std::make_unique( + platform_view_delegate, shell.GetTaskRunners()); + ON_CALL(*result, CreateRenderingSurface()) + .WillByDefault(::testing::Invoke( + [] { return std::make_unique(); })); + return result; + }, + [](Shell& shell) { return std::make_unique(shell); }); + ASSERT_NE(nullptr, spawn.get()); + ASSERT_TRUE(ValidateShell(spawn.get())); + DestroyShell(std::move(spawn)); + latch.Signal(); + }); + latch.Wait(); + } + + DestroyShell(std::move(shell)); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 4087f88b6715a..b964b191d6958 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -28,6 +28,7 @@ _flutter_framework_headers = [ "framework/Headers/FlutterCallbackCache.h", "framework/Headers/FlutterDartProject.h", "framework/Headers/FlutterEngine.h", + "framework/Headers/FlutterEngineGroup.h", "framework/Headers/FlutterHeadlessDartRunner.h", "framework/Headers/FlutterPlatformViews.h", "framework/Headers/FlutterPlugin.h", @@ -52,6 +53,7 @@ source_set("flutter_framework_source") { "framework/Source/FlutterDartProject.mm", "framework/Source/FlutterDartProject_Internal.h", "framework/Source/FlutterEngine.mm", + "framework/Source/FlutterEngineGroup.mm", "framework/Source/FlutterEngine_Internal.h", "framework/Source/FlutterHeadlessDartRunner.mm", "framework/Source/FlutterObservatoryPublisher.h", @@ -215,6 +217,7 @@ shared_library("ios_test_flutter") { "framework/Source/FlutterAppDelegateTest.mm", "framework/Source/FlutterBinaryMessengerRelayTest.mm", "framework/Source/FlutterDartProjectTest.mm", + "framework/Source/FlutterEngineGroupTest.mm", "framework/Source/FlutterEngineTest.mm", "framework/Source/FlutterPluginAppLifeCycleDelegateTest.m", "framework/Source/FlutterTextInputPluginTest.m", diff --git a/shell/platform/darwin/ios/framework/Headers/Flutter.h b/shell/platform/darwin/ios/framework/Headers/Flutter.h index 96409628029fd..cd59a82016d6f 100644 --- a/shell/platform/darwin/ios/framework/Headers/Flutter.h +++ b/shell/platform/darwin/ios/framework/Headers/Flutter.h @@ -12,6 +12,7 @@ #import "FlutterCodecs.h" #import "FlutterDartProject.h" #import "FlutterEngine.h" +#import "FlutterEngineGroup.h" #import "FlutterHeadlessDartRunner.h" #import "FlutterMacros.h" #import "FlutterPlatformViews.h" diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h new file mode 100644 index 0000000000000..4ea645e151ba2 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +#import "FlutterEngine.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Represents a collection of FlutterEngines who share resources which allows + * them to be created with less time const and occupy less memory than just + * creating multiple FlutterEngines. + * + * @see http://flutter.dev/go/multiple-engines + */ +@interface FlutterEngineGroup : NSObject +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithName:(NSString*)name + project:(nullable FlutterDartProject*)project NS_DESIGNATED_INITIALIZER; +- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint; +@end + +NS_ASSUME_NONNULL_END diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 5ece908bf5993..8575cae6852d3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -34,6 +34,7 @@ NSString* const FlutterDefaultDartEntrypoint = nil; NSString* const FlutterDefaultInitialRoute = nil; +NSString* const FlutterEngineWillDealloc = @"FlutterEngineWillDealloc"; static constexpr int kNumProfilerSamplesPerSec = 5; @interface FlutterEngineRegistrar : NSObject @@ -54,7 +55,7 @@ @interface FlutterEngine () @implementation FlutterEngine { fml::scoped_nsobject _dartProject; - flutter::ThreadHost _threadHost; + std::shared_ptr _threadHost; std::unique_ptr _shell; NSString* _labelPrefix; std::unique_ptr> _weakFactory; @@ -64,8 +65,8 @@ @implementation FlutterEngine { std::shared_ptr _platformViewsController; flutter::IOSRenderingAPI _renderingApi; - std::unique_ptr _profiler_metrics; - std::unique_ptr _profiler; + std::shared_ptr _profiler_metrics; + std::shared_ptr _profiler; // Channels fml::scoped_nsobject _platformPlugin; @@ -181,6 +182,10 @@ - (void)dealloc { } }]; + [[NSNotificationCenter defaultCenter] postNotificationName:FlutterEngineWillDealloc + object:self + userInfo:nil]; + /// nil out weak references. [_registrars enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) { @@ -306,7 +311,7 @@ - (void)destroyContext { self.isolateId = nil; _shell.reset(); _profiler.reset(); - _threadHost.Reset(); + _threadHost.reset(); _platformViewsController.reset(); } @@ -368,10 +373,10 @@ - (void)resetChannels { } - (void)startProfiler { - FML_DCHECK(!_threadHost.name_prefix.empty()); - _profiler_metrics = std::make_unique(); - _profiler = std::make_unique( - _threadHost.name_prefix.c_str(), _threadHost.profiler_thread->GetTaskRunner(), + FML_DCHECK(!_threadHost->name_prefix.empty()); + _profiler_metrics = std::make_shared(); + _profiler = std::make_shared( + _threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(), [self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec); _profiler->Start(); } @@ -550,7 +555,8 @@ - (BOOL)createShell:(NSString*)entrypoint } NSString* threadLabel = [FlutterEngine generateThreadLabel:_labelPrefix]; - _threadHost = [FlutterEngine makeThreadHost:threadLabel]; + _threadHost = std::make_shared(); + *_threadHost = [FlutterEngine makeThreadHost:threadLabel]; // Lambda captures by pointers to ObjC objects are fine here because the // create call is synchronous. @@ -566,9 +572,9 @@ - (BOOL)createShell:(NSString*)entrypoint flutter::TaskRunners task_runners(threadLabel.UTF8String, // label fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform - _threadHost.raster_thread->GetTaskRunner(), // raster - _threadHost.ui_thread->GetTaskRunner(), // ui - _threadHost.io_thread->GetTaskRunner() // io + _threadHost->raster_thread->GetTaskRunner(), // raster + _threadHost->ui_thread->GetTaskRunner(), // ui + _threadHost->io_thread->GetTaskRunner() // io ); // Create the shell. This is a blocking operation. @@ -921,6 +927,42 @@ - (void)waitForFirstFrame:(NSTimeInterval)timeout }); } +- (FlutterEngine*)spawnWithEntrypoint:(NSString*)entrypoint { + assert(_shell); + FlutterEngine* result = + [[FlutterEngine alloc] initWithName:[_labelPrefix stringByAppendingString:@"-spawn"] + project:_dartProject.get() + allowHeadlessExecution:_allowHeadlessExecution]; + + flutter::Settings settings = _shell->GetSettings(); + if (entrypoint) { + settings.advisory_script_entrypoint = entrypoint.UTF8String; + settings.advisory_script_uri = std::string("main.dart"); + } else { + settings.advisory_script_entrypoint = std::string("main"); + settings.advisory_script_uri = std::string("main.dart"); + } + + flutter::Shell::CreateCallback on_create_platform_view = + [self](flutter::Shell& shell) { + [self recreatePlatformViewController]; + return std::make_unique( + shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners()); + }; + + flutter::Shell::CreateCallback on_create_rasterizer = + [](flutter::Shell& shell) { return std::make_unique(shell); }; + + std::unique_ptr shell = + _shell->Spawn(std::move(settings), on_create_platform_view, on_create_rasterizer); + + result->_threadHost = _threadHost; + result->_profiler = _profiler; + result->_profiler_metrics = _profiler_metrics; + [result setupShell:std::move(shell) withObservatoryPublication:NO]; + return result; +} + @end @implementation FlutterEngineRegistrar { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm new file mode 100644 index 0000000000000..ba68eb0f6efe1 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h" + +@interface FlutterEngineGroup () +@property(nonatomic, copy) NSString* name; +@property(nonatomic, strong) NSMutableArray* engines; +@property(nonatomic, strong) FlutterDartProject* project; +@end + +@implementation FlutterEngineGroup { + int _engineCount; +} + +- (instancetype)initWithName:(NSString*)name project:(nullable FlutterDartProject*)project { + self = [super init]; + if (self) { + self.name = name; + self.engines = [[NSMutableArray alloc] init]; + self.project = project; + } + return self; +} + +- (void)dealloc { + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center removeObserver:self]; + [_name release]; + [_engines release]; + [super dealloc]; +} + +- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint { + NSString* engineName = [NSString stringWithFormat:@"%@.%d", self.name, ++_engineCount]; + FlutterEngine* engine; + if (self.engines.count <= 0) { + engine = [[FlutterEngine alloc] initWithName:engineName project:self.project]; + [engine run]; + } else { + FlutterEngine* spawner = (FlutterEngine*)[self.engines[0] pointerValue]; + engine = [spawner spawnWithEntrypoint:entrypoint]; + } + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center addObserver:self + selector:@selector(onEngineWillBeDealloced:) + name:FlutterEngineWillDealloc + object:engine]; + + return [engine autorelease]; +} + +- (void)onEngineWillBeDealloced:(NSNotification*)notification { + [_engines removeObject:[NSValue valueWithPointer:notification.object]]; +} + +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm new file mode 100644 index 0000000000000..adcbdcf0e1ad7 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h" + +FLUTTER_ASSERT_ARC + +@interface FlutterEngineGroupTest : XCTestCase +@end + +@implementation FlutterEngineGroupTest + +- (void)testMake { + FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil]; + FlutterEngine* engine = [group makeEngineWithEntrypoint:nil]; + XCTAssertNotNil(engine); +} + +- (void)testSpawn { + FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil]; + FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil]; + FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil]; + XCTAssertNotNil(spawner); + XCTAssertNotNil(spawnee); +} + +- (void)testDeleteLastEngine { + FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil]; + @autoreleasepool { + FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil]; + XCTAssertNotNil(spawner); + } + FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil]; + XCTAssertNotNil(spawnee); +} + +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index 3684ea5374a60..8d35c2c583f4f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -134,4 +134,28 @@ - (void)testWaitForFirstFrameTimeout { [self waitForExpectationsWithTimeout:1 handler:nil]; } +- (void)testSpawn { + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"]; + [engine run]; + FlutterEngine* spawn = [engine spawnWithEntrypoint:nil]; + XCTAssertNotNil(spawn); +} + +- (void)testDeallocNotification { + XCTestExpectation* deallocNotification = [self expectationWithDescription:@"deallocNotification"]; + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + id observer; + @autoreleasepool { + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"]; + observer = [center addObserverForName:FlutterEngineWillDealloc + object:engine + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* note) { + [deallocNotification fulfill]; + }]; + } + [self waitForExpectationsWithTimeout:1 handler:nil]; + [center removeObserver:observer]; +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h index 85a82f3891530..c91dda6115ba2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h @@ -22,6 +22,8 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" #import "flutter/shell/platform/darwin/ios/platform_view_ios.h" +extern NSString* const FlutterEngineWillDealloc; + @interface FlutterEngine () - (flutter::Shell&)shell; @@ -49,6 +51,15 @@ - (flutter::PlatformViewIOS*)iosPlatformView; - (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback; + +/** + * Creates one FlutterEngine from another, sharing components between them. + * This results in a faster creation time and a smaller memory footprint engine. + * + * @see http://flutter.dev/go/multiple-engines + */ +- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entryPoint; + @end #endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERENGINE_INTERNAL_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h index 50db832916ea4..caa96ce1ed369 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h @@ -5,6 +5,8 @@ #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/ios/rendering_api_selection.h" +extern NSString* const FlutterEngineWillDealloc; + @class FlutterBinaryMessengerRelay; // Category to add test-only visibility. @@ -12,4 +14,5 @@ - (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger; - (flutter::IOSRenderingAPI)platformViewsRenderingAPI; - (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback; +- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entryPoint; @end From a3091d40be9341f7468ad7ef5325404a12af2557 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 11 Dec 2020 14:37:19 -0800 Subject: [PATCH 2/8] xiao feedback 1 --- shell/common/shell.h | 2 +- .../ios/framework/Headers/FlutterEngineGroup.h | 16 ++++++++++++++++ .../darwin/ios/framework/Source/FlutterEngine.mm | 2 +- .../ios/framework/Source/FlutterEngineGroup.mm | 4 ++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/shell/common/shell.h b/shell/common/shell.h index 0c12e5e4c8613..2166450f09656 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -222,7 +222,7 @@ class Shell final : public PlatformView::Delegate, //---------------------------------------------------------------------------- /// @brief Creates one Shell from another Shell where the created Shell - /// takes the opportunity share any internal components it can. + /// takes the opportunity to share any internal components it can. /// This results is a Shell that has a smaller startup time cost /// and a smaller memory footprint than an Shell created with a /// Create function. diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h index 4ea645e151ba2..4032c8283fa51 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h @@ -13,12 +13,28 @@ NS_ASSUME_NONNULL_BEGIN * them to be created with less time const and occupy less memory than just * creating multiple FlutterEngines. * + * Deleting a FlutterEngineGroup doesn't invalidate existing FlutterEngines, but + * it eliminates the possibility to create more FlutterEngines in that group. + * * @see http://flutter.dev/go/multiple-engines */ @interface FlutterEngineGroup : NSObject - (instancetype)init NS_UNAVAILABLE; + +/** + * Initialize a new FlutterEngineGroup. + * + * @param name The name that will present in the threads shared across the + * engines in this group. + * @param project The `FlutterDartProject` that all FlutterEngines in this group + * will be executing. + */ - (instancetype)initWithName:(NSString*)name project:(nullable FlutterDartProject*)project NS_DESIGNATED_INITIALIZER; + +/** + * Creates a running `FlutterEngine` that exists in this group. + */ - (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 8575cae6852d3..000b5f4c39186 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -928,7 +928,7 @@ - (void)waitForFirstFrame:(NSTimeInterval)timeout } - (FlutterEngine*)spawnWithEntrypoint:(NSString*)entrypoint { - assert(_shell); + NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run)."); FlutterEngine* result = [[FlutterEngine alloc] initWithName:[_labelPrefix stringByAppendingString:@"-spawn"] project:_dartProject.get() diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm index ba68eb0f6efe1..e64f2c3ba8900 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm @@ -12,7 +12,7 @@ @interface FlutterEngineGroup () @end @implementation FlutterEngineGroup { - int _engineCount; + int _enginesCreatedCount; } - (instancetype)initWithName:(NSString*)name project:(nullable FlutterDartProject*)project { @@ -34,7 +34,7 @@ - (void)dealloc { } - (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint { - NSString* engineName = [NSString stringWithFormat:@"%@.%d", self.name, ++_engineCount]; + NSString* engineName = [NSString stringWithFormat:@"%@.%d", self.name, ++_enginesCreatedCount]; FlutterEngine* engine; if (self.engines.count <= 0) { engine = [[FlutterEngine alloc] initWithName:engineName project:self.project]; From 0923668dfa66d5e5bcd063d01e0b0de30f3a9928 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 11 Dec 2020 14:46:36 -0800 Subject: [PATCH 3/8] xiao feedback 2 --- shell/platform/darwin/ios/framework/Source/FlutterEngine.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 000b5f4c39186..1bff7e4aa66f4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -930,7 +930,7 @@ - (void)waitForFirstFrame:(NSTimeInterval)timeout - (FlutterEngine*)spawnWithEntrypoint:(NSString*)entrypoint { NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run)."); FlutterEngine* result = - [[FlutterEngine alloc] initWithName:[_labelPrefix stringByAppendingString:@"-spawn"] + [[FlutterEngine alloc] initWithName:_labelPrefix project:_dartProject.get() allowHeadlessExecution:_allowHeadlessExecution]; From e5dd242ce34e969e55446e533377b7ed087120d6 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 11 Dec 2020 15:05:42 -0800 Subject: [PATCH 4/8] xiao feedback 3 - added libaryuri support --- .../framework/Headers/FlutterEngineGroup.h | 3 +- .../ios/framework/Source/FlutterEngine.mm | 44 +++++++++---------- .../framework/Source/FlutterEngineGroup.mm | 7 +-- .../Source/FlutterEngineGroupTest.mm | 10 ++--- .../ios/framework/Source/FlutterEngineTest.mm | 2 +- .../framework/Source/FlutterEngine_Internal.h | 3 +- .../ios/framework/Source/FlutterEngine_Test.h | 3 +- 7 files changed, 37 insertions(+), 35 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h index 4032c8283fa51..550dc36a03b0c 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h @@ -35,7 +35,8 @@ NS_ASSUME_NONNULL_BEGIN /** * Creates a running `FlutterEngine` that exists in this group. */ -- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint; +- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint + libraryURI:(nullable NSString*)libraryURI; @end NS_ASSUME_NONNULL_END diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 1bff7e4aa66f4..f5864ddb639eb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -527,6 +527,20 @@ + (NSString*)generateThreadLabel:(NSString*)labelPrefix { threadHostType}; } +static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSString* libraryURI) { + if (libraryURI) { + FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library"; + settings->advisory_script_entrypoint = entrypoint.UTF8String; + settings->advisory_script_uri = libraryURI.UTF8String; + } else if (entrypoint) { + settings->advisory_script_entrypoint = entrypoint.UTF8String; + settings->advisory_script_uri = std::string("main.dart"); + } else { + settings->advisory_script_entrypoint = std::string("main"); + settings->advisory_script_uri = std::string("main.dart"); + } +} + - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI initialRoute:(NSString*)initialRoute { @@ -542,17 +556,7 @@ - (BOOL)createShell:(NSString*)entrypoint auto platformData = [_dartProject.get() defaultPlatformData]; - if (libraryURI) { - FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library"; - settings.advisory_script_entrypoint = entrypoint.UTF8String; - settings.advisory_script_uri = libraryURI.UTF8String; - } else if (entrypoint) { - settings.advisory_script_entrypoint = entrypoint.UTF8String; - settings.advisory_script_uri = std::string("main.dart"); - } else { - settings.advisory_script_entrypoint = std::string("main"); - settings.advisory_script_uri = std::string("main.dart"); - } + SetEntryPoint(&settings, entrypoint, libraryURI); NSString* threadLabel = [FlutterEngine generateThreadLabel:_labelPrefix]; _threadHost = std::make_shared(); @@ -927,21 +931,15 @@ - (void)waitForFirstFrame:(NSTimeInterval)timeout }); } -- (FlutterEngine*)spawnWithEntrypoint:(NSString*)entrypoint { +- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint + libraryURI:(/*nullable*/ NSString*)libraryURI { NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run)."); - FlutterEngine* result = - [[FlutterEngine alloc] initWithName:_labelPrefix - project:_dartProject.get() - allowHeadlessExecution:_allowHeadlessExecution]; + FlutterEngine* result = [[FlutterEngine alloc] initWithName:_labelPrefix + project:_dartProject.get() + allowHeadlessExecution:_allowHeadlessExecution]; flutter::Settings settings = _shell->GetSettings(); - if (entrypoint) { - settings.advisory_script_entrypoint = entrypoint.UTF8String; - settings.advisory_script_uri = std::string("main.dart"); - } else { - settings.advisory_script_entrypoint = std::string("main"); - settings.advisory_script_uri = std::string("main.dart"); - } + SetEntryPoint(&settings, entrypoint, libraryURI); flutter::Shell::CreateCallback on_create_platform_view = [self](flutter::Shell& shell) { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm index e64f2c3ba8900..8d8f806d559d3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm @@ -33,15 +33,16 @@ - (void)dealloc { [super dealloc]; } -- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint { +- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint + libraryURI:(nullable NSString*)libraryURI { NSString* engineName = [NSString stringWithFormat:@"%@.%d", self.name, ++_enginesCreatedCount]; FlutterEngine* engine; if (self.engines.count <= 0) { engine = [[FlutterEngine alloc] initWithName:engineName project:self.project]; - [engine run]; + [engine runWithEntrypoint:entrypoint libraryURI:libraryURI]; } else { FlutterEngine* spawner = (FlutterEngine*)[self.engines[0] pointerValue]; - engine = [spawner spawnWithEntrypoint:entrypoint]; + engine = [spawner spawnWithEntrypoint:entrypoint libraryURI:libraryURI]; } NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm index adcbdcf0e1ad7..213e65cff57f7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm @@ -16,14 +16,14 @@ @implementation FlutterEngineGroupTest - (void)testMake { FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil]; - FlutterEngine* engine = [group makeEngineWithEntrypoint:nil]; + FlutterEngine* engine = [group makeEngineWithEntrypoint:nil libraryURI:nil]; XCTAssertNotNil(engine); } - (void)testSpawn { FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil]; - FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil]; - FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil]; + FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil libraryURI:nil]; + FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil libraryURI:nil]; XCTAssertNotNil(spawner); XCTAssertNotNil(spawnee); } @@ -31,10 +31,10 @@ - (void)testSpawn { - (void)testDeleteLastEngine { FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil]; @autoreleasepool { - FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil]; + FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil libraryURI:nil]; XCTAssertNotNil(spawner); } - FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil]; + FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil libraryURI:nil]; XCTAssertNotNil(spawnee); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index 8d35c2c583f4f..dd63da24de014 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -137,7 +137,7 @@ - (void)testWaitForFirstFrameTimeout { - (void)testSpawn { FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"]; [engine run]; - FlutterEngine* spawn = [engine spawnWithEntrypoint:nil]; + FlutterEngine* spawn = [engine spawnWithEntrypoint:nil libraryURI:nil]; XCTAssertNotNil(spawn); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h index c91dda6115ba2..7ce960769a4dd 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h @@ -58,7 +58,8 @@ extern NSString* const FlutterEngineWillDealloc; * * @see http://flutter.dev/go/multiple-engines */ -- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entryPoint; +- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint + libraryURI:(/*nullable*/ NSString*)libraryURI; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h index caa96ce1ed369..e1220d42acf76 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h @@ -14,5 +14,6 @@ extern NSString* const FlutterEngineWillDealloc; - (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger; - (flutter::IOSRenderingAPI)platformViewsRenderingAPI; - (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback; -- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entryPoint; +- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint + libraryURI:(/*nullable*/ NSString*)libraryURI; @end From f3916b01ba8991d0752293dccdf3013ec80ceb01 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 11 Dec 2020 16:30:12 -0800 Subject: [PATCH 5/8] added FlutterEngineGroup to expected output docs --- tools/gen_objcdoc.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/gen_objcdoc.sh b/tools/gen_objcdoc.sh index abeb10980425a..5d453ed014252 100755 --- a/tools/gen_objcdoc.sh +++ b/tools/gen_objcdoc.sh @@ -46,6 +46,7 @@ FlutterBasicMessageChannel.html FlutterCallbackCache.html FlutterCallbackInformation.html FlutterDartProject.html +FlutterEngineGroup.html FlutterEngine.html FlutterError.html FlutterEventChannel.html From 030b1f4dc0156f95f77c1cafb6c7129ab7e80a7e Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 11 Dec 2020 17:27:51 -0800 Subject: [PATCH 6/8] sorted gen objcdoc correctly --- tools/gen_objcdoc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/gen_objcdoc.sh b/tools/gen_objcdoc.sh index 5d453ed014252..6d51a5553264d 100755 --- a/tools/gen_objcdoc.sh +++ b/tools/gen_objcdoc.sh @@ -46,8 +46,8 @@ FlutterBasicMessageChannel.html FlutterCallbackCache.html FlutterCallbackInformation.html FlutterDartProject.html -FlutterEngineGroup.html FlutterEngine.html +FlutterEngineGroup.html FlutterError.html FlutterEventChannel.html FlutterHeadlessDartRunner.html From d56e93a83c5d9cd34010b0e3eca72ff07db55b38 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 14 Dec 2020 10:47:17 -0800 Subject: [PATCH 7/8] xiao feedback 4 --- .../darwin/ios/framework/Headers/FlutterEngineGroup.h | 7 +++++-- .../darwin/ios/framework/Source/FlutterEngine_Internal.h | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h index 550dc36a03b0c..82b681c03c63d 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h @@ -16,7 +16,8 @@ NS_ASSUME_NONNULL_BEGIN * Deleting a FlutterEngineGroup doesn't invalidate existing FlutterEngines, but * it eliminates the possibility to create more FlutterEngines in that group. * - * @see http://flutter.dev/go/multiple-engines + * @warning This class is a work-in-progress and may change. + * @see https://github.com/flutter/flutter/issues/72009 */ @interface FlutterEngineGroup : NSObject - (instancetype)init NS_UNAVAILABLE; @@ -33,7 +34,9 @@ NS_ASSUME_NONNULL_BEGIN project:(nullable FlutterDartProject*)project NS_DESIGNATED_INITIALIZER; /** - * Creates a running `FlutterEngine` that exists in this group. + * Creates a running `FlutterEngine` that shares components with this group. + * + * @see FlutterEngineGroup */ - (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint libraryURI:(nullable NSString*)libraryURI; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h index 7ce960769a4dd..36c3b2c433834 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h @@ -53,10 +53,10 @@ extern NSString* const FlutterEngineWillDealloc; - (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback; /** - * Creates one FlutterEngine from another, sharing components between them. - * This results in a faster creation time and a smaller memory footprint engine. + * Creates one running FlutterEngine from another, sharing components between them. * - * @see http://flutter.dev/go/multiple-engines + * This results in a faster creation time and a smaller memory footprint engine. + * This should only be called on a FlutterEngine that is running. */ - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint libraryURI:(/*nullable*/ NSString*)libraryURI; From 255e50454a4751ef5d5b2ac102555c5c07547d78 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 14 Dec 2020 10:55:20 -0800 Subject: [PATCH 8/8] xiao feedback 5 --- shell/platform/darwin/ios/framework/Source/FlutterEngine.mm | 4 ++++ .../darwin/ios/framework/Source/FlutterEngineGroup.mm | 1 + .../darwin/ios/framework/Source/FlutterEngineGroupTest.mm | 2 ++ .../darwin/ios/framework/Source/FlutterEngine_Test.h | 5 +++++ 4 files changed, 12 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index f5864ddb639eb..857ad0e486a4d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -961,6 +961,10 @@ - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint return result; } +- (const flutter::ThreadHost&)threadHost { + return *_threadHost; +} + @end @implementation FlutterEngineRegistrar { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm index 8d8f806d559d3..8b12094a4c4de 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm @@ -44,6 +44,7 @@ - (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint FlutterEngine* spawner = (FlutterEngine*)[self.engines[0] pointerValue]; engine = [spawner spawnWithEntrypoint:entrypoint libraryURI:libraryURI]; } + [_engines addObject:[NSValue valueWithPointer:engine]]; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center addObserver:self diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm index 213e65cff57f7..dd34a667f80c1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm @@ -6,6 +6,7 @@ #import #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h" FLUTTER_ASSERT_ARC @@ -26,6 +27,7 @@ - (void)testSpawn { FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil libraryURI:nil]; XCTAssertNotNil(spawner); XCTAssertNotNil(spawnee); + XCTAssertEqual(&spawner.threadHost, &spawnee.threadHost); } - (void)testDeleteLastEngine { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h index e1220d42acf76..e241d22b1e8da 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h @@ -9,6 +9,10 @@ extern NSString* const FlutterEngineWillDealloc; @class FlutterBinaryMessengerRelay; +namespace flutter { +class ThreadHost; +} + // Category to add test-only visibility. @interface FlutterEngine (Test) - (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger; @@ -16,4 +20,5 @@ extern NSString* const FlutterEngineWillDealloc; - (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback; - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint libraryURI:(/*nullable*/ NSString*)libraryURI; +- (const flutter::ThreadHost&)threadHost; @end