mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Implemented FlutterEngineGroup and Spawn API. (flutter/engine#22975)
This commit is contained in:
parent
525acfa06b
commit
3d4aa5548a
@ -946,6 +946,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
|
||||
@ -964,6 +965,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
|
||||
|
||||
@ -436,6 +436,21 @@ Shell::~Shell() {
|
||||
platform_latch.Wait();
|
||||
}
|
||||
|
||||
std::unique_ptr<Shell> Shell::Spawn(
|
||||
Settings settings,
|
||||
const CreateCallback<PlatformView>& on_create_platform_view,
|
||||
const CreateCallback<Rasterizer>& on_create_rasterizer) {
|
||||
RunConfiguration configuration =
|
||||
RunConfiguration::InferFromSettings(settings);
|
||||
TaskRunners task_runners = task_runners_;
|
||||
FML_DCHECK(task_runners.IsValid());
|
||||
std::unique_ptr<Shell> 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",
|
||||
|
||||
@ -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 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.
|
||||
///
|
||||
/// @see http://flutter.dev/go/multiple-engines
|
||||
std::unique_ptr<Shell> Spawn(
|
||||
Settings settings,
|
||||
const CreateCallback<PlatformView>& on_create_platform_view,
|
||||
const CreateCallback<Rasterizer>& on_create_rasterizer);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Starts an isolate for the given RunConfiguration.
|
||||
///
|
||||
|
||||
@ -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> 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<PlatformMessage> message));
|
||||
|
||||
MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket,
|
||||
void(std::unique_ptr<PointerDataPacket> packet));
|
||||
|
||||
MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction,
|
||||
void(int32_t id,
|
||||
SemanticsAction action,
|
||||
std::vector<uint8_t> args));
|
||||
|
||||
MOCK_METHOD1(OnPlatformViewSetSemanticsEnabled, void(bool enabled));
|
||||
|
||||
MOCK_METHOD1(OnPlatformViewSetAccessibilityFeatures, void(int32_t flags));
|
||||
|
||||
MOCK_METHOD1(OnPlatformViewRegisterTexture,
|
||||
void(std::shared_ptr<Texture> 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<const fml::Mapping> snapshot_data,
|
||||
std::unique_ptr<const fml::Mapping> 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<AssetManager> asset_manager));
|
||||
};
|
||||
|
||||
class MockSurface : public Surface {
|
||||
MOCK_METHOD0(IsValid, bool());
|
||||
|
||||
MOCK_METHOD1(AcquireFrame,
|
||||
std::unique_ptr<SurfaceFrame>(const SkISize& size));
|
||||
|
||||
MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix());
|
||||
|
||||
MOCK_METHOD0(GetContext, GrDirectContext*());
|
||||
|
||||
MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr<GLContextResult>());
|
||||
|
||||
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<Surface>());
|
||||
};
|
||||
} // 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<MockPlatformView>(
|
||||
platform_view_delegate, shell.GetTaskRunners());
|
||||
ON_CALL(*result, CreateRenderingSurface())
|
||||
.WillByDefault(::testing::Invoke(
|
||||
[] { return std::make_unique<MockSurface>(); }));
|
||||
return result;
|
||||
},
|
||||
[](Shell& shell) { return std::make_unique<Rasterizer>(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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
// 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 <Foundation/Foundation.h>
|
||||
|
||||
#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.
|
||||
*
|
||||
* Deleting a FlutterEngineGroup doesn't invalidate existing FlutterEngines, but
|
||||
* it eliminates the possibility to create more FlutterEngines in that group.
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* 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 shares components with this group.
|
||||
*
|
||||
* @see FlutterEngineGroup
|
||||
*/
|
||||
- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint
|
||||
libraryURI:(nullable NSString*)libraryURI;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -34,6 +34,7 @@
|
||||
|
||||
NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
NSString* const FlutterDefaultInitialRoute = nil;
|
||||
NSString* const FlutterEngineWillDealloc = @"FlutterEngineWillDealloc";
|
||||
static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
|
||||
@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
|
||||
@ -54,7 +55,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
|
||||
@implementation FlutterEngine {
|
||||
fml::scoped_nsobject<FlutterDartProject> _dartProject;
|
||||
flutter::ThreadHost _threadHost;
|
||||
std::shared_ptr<flutter::ThreadHost> _threadHost;
|
||||
std::unique_ptr<flutter::Shell> _shell;
|
||||
NSString* _labelPrefix;
|
||||
std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory;
|
||||
@ -64,8 +65,8 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
|
||||
std::shared_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
|
||||
flutter::IOSRenderingAPI _renderingApi;
|
||||
std::unique_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
|
||||
std::unique_ptr<flutter::SamplingProfiler> _profiler;
|
||||
std::shared_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
|
||||
std::shared_ptr<flutter::SamplingProfiler> _profiler;
|
||||
|
||||
// Channels
|
||||
fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin;
|
||||
@ -181,6 +182,10 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
}
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:FlutterEngineWillDealloc
|
||||
object:self
|
||||
userInfo:nil];
|
||||
|
||||
/// nil out weak references.
|
||||
[_registrars
|
||||
enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
|
||||
@ -306,7 +311,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
self.isolateId = nil;
|
||||
_shell.reset();
|
||||
_profiler.reset();
|
||||
_threadHost.Reset();
|
||||
_threadHost.reset();
|
||||
_platformViewsController.reset();
|
||||
}
|
||||
|
||||
@ -368,10 +373,10 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
}
|
||||
|
||||
- (void)startProfiler {
|
||||
FML_DCHECK(!_threadHost.name_prefix.empty());
|
||||
_profiler_metrics = std::make_unique<flutter::ProfilerMetricsIOS>();
|
||||
_profiler = std::make_unique<flutter::SamplingProfiler>(
|
||||
_threadHost.name_prefix.c_str(), _threadHost.profiler_thread->GetTaskRunner(),
|
||||
FML_DCHECK(!_threadHost->name_prefix.empty());
|
||||
_profiler_metrics = std::make_shared<flutter::ProfilerMetricsIOS>();
|
||||
_profiler = std::make_shared<flutter::SamplingProfiler>(
|
||||
_threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
|
||||
[self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec);
|
||||
_profiler->Start();
|
||||
}
|
||||
@ -522,6 +527,20 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
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 {
|
||||
@ -537,20 +556,11 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
|
||||
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 = [FlutterEngine makeThreadHost:threadLabel];
|
||||
_threadHost = std::make_shared<flutter::ThreadHost>();
|
||||
*_threadHost = [FlutterEngine makeThreadHost:threadLabel];
|
||||
|
||||
// Lambda captures by pointers to ObjC objects are fine here because the
|
||||
// create call is synchronous.
|
||||
@ -566,9 +576,9 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
|
||||
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 +931,40 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
});
|
||||
}
|
||||
|
||||
- (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];
|
||||
|
||||
flutter::Settings settings = _shell->GetSettings();
|
||||
SetEntryPoint(&settings, entrypoint, libraryURI);
|
||||
|
||||
flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
|
||||
[self](flutter::Shell& shell) {
|
||||
[self recreatePlatformViewController];
|
||||
return std::make_unique<flutter::PlatformViewIOS>(
|
||||
shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners());
|
||||
};
|
||||
|
||||
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
|
||||
[](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
|
||||
|
||||
std::unique_ptr<flutter::Shell> 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;
|
||||
}
|
||||
|
||||
- (const flutter::ThreadHost&)threadHost {
|
||||
return *_threadHost;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FlutterEngineRegistrar {
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
// 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<NSValue*>* engines;
|
||||
@property(nonatomic, strong) FlutterDartProject* project;
|
||||
@end
|
||||
|
||||
@implementation FlutterEngineGroup {
|
||||
int _enginesCreatedCount;
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString*)name project:(nullable FlutterDartProject*)project {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.name = name;
|
||||
self.engines = [[NSMutableArray<NSValue*> 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
|
||||
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 runWithEntrypoint:entrypoint libraryURI:libraryURI];
|
||||
} else {
|
||||
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
|
||||
selector:@selector(onEngineWillBeDealloced:)
|
||||
name:FlutterEngineWillDealloc
|
||||
object:engine];
|
||||
|
||||
return [engine autorelease];
|
||||
}
|
||||
|
||||
- (void)onEngineWillBeDealloced:(NSNotification*)notification {
|
||||
[_engines removeObject:[NSValue valueWithPointer:notification.object]];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,43 @@
|
||||
// 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 <OCMock/OCMock.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.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 libraryURI:nil];
|
||||
XCTAssertNotNil(engine);
|
||||
}
|
||||
|
||||
- (void)testSpawn {
|
||||
FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil];
|
||||
FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil libraryURI:nil];
|
||||
FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil libraryURI:nil];
|
||||
XCTAssertNotNil(spawner);
|
||||
XCTAssertNotNil(spawnee);
|
||||
XCTAssertEqual(&spawner.threadHost, &spawnee.threadHost);
|
||||
}
|
||||
|
||||
- (void)testDeleteLastEngine {
|
||||
FlutterEngineGroup* group = [[FlutterEngineGroup alloc] initWithName:@"foo" project:nil];
|
||||
@autoreleasepool {
|
||||
FlutterEngine* spawner = [group makeEngineWithEntrypoint:nil libraryURI:nil];
|
||||
XCTAssertNotNil(spawner);
|
||||
}
|
||||
FlutterEngine* spawnee = [group makeEngineWithEntrypoint:nil libraryURI:nil];
|
||||
XCTAssertNotNil(spawnee);
|
||||
}
|
||||
|
||||
@end
|
||||
@ -134,4 +134,28 @@ FLUTTER_ASSERT_ARC
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
}
|
||||
|
||||
- (void)testSpawn {
|
||||
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
|
||||
[engine run];
|
||||
FlutterEngine* spawn = [engine spawnWithEntrypoint:nil libraryURI:nil];
|
||||
XCTAssertNotNil(spawn);
|
||||
}
|
||||
|
||||
- (void)testDeallocNotification {
|
||||
XCTestExpectation* deallocNotification = [self expectationWithDescription:@"deallocNotification"];
|
||||
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
||||
id<NSObject> 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
|
||||
|
||||
@ -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 () <FlutterViewEngineDelegate>
|
||||
|
||||
- (flutter::Shell&)shell;
|
||||
@ -49,6 +51,16 @@
|
||||
- (flutter::PlatformViewIOS*)iosPlatformView;
|
||||
|
||||
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;
|
||||
|
||||
/**
|
||||
* Creates one running FlutterEngine from another, sharing components between them.
|
||||
*
|
||||
* 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;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERENGINE_INTERNAL_H_
|
||||
|
||||
@ -5,11 +5,20 @@
|
||||
#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;
|
||||
|
||||
namespace flutter {
|
||||
class ThreadHost;
|
||||
}
|
||||
|
||||
// Category to add test-only visibility.
|
||||
@interface FlutterEngine (Test) <FlutterBinaryMessenger>
|
||||
- (void)setBinaryMessenger:(FlutterBinaryMessengerRelay*)binaryMessenger;
|
||||
- (flutter::IOSRenderingAPI)platformViewsRenderingAPI;
|
||||
- (void)waitForFirstFrame:(NSTimeInterval)timeout callback:(void (^)(BOOL didTimeout))callback;
|
||||
- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint
|
||||
libraryURI:(/*nullable*/ NSString*)libraryURI;
|
||||
- (const flutter::ThreadHost&)threadHost;
|
||||
@end
|
||||
|
||||
@ -47,6 +47,7 @@ FlutterCallbackCache.html
|
||||
FlutterCallbackInformation.html
|
||||
FlutterDartProject.html
|
||||
FlutterEngine.html
|
||||
FlutterEngineGroup.html
|
||||
FlutterError.html
|
||||
FlutterEventChannel.html
|
||||
FlutterHeadlessDartRunner.html
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user