流程图

FlutterEngineGroup 引擎启动流程(iOS)

创立 FlutterEngineGroup

Flutter 引擎组的效果

  • 1、首要用于办理多个 Flutter 引擎,从 FlutterEngineGroup 生成的 FlutterEngine 具有常用共享资源(例如 GPU 上下文、字体度量和隔离线程的快照)的功用优势,然后加速首次烘托的速度、下降推迟并下降内存占用。
  • 2、官方说:选用 Group 办理多引擎在内存上,除了第一个 Engine 目标之外,后续每个 Engine 目标在 Android 和 iOS 上仅占用 180kB 。“实际上是否,待验证” 。
  • 3、比如 let engines = FlutterEngineGroup(name: “multiple-flutters”, project: nil)

FlutterEngineGroup 源码

  • 从源码上看 FlutterEngineGroup 类首要有一个 engines 数组,去维护办理这些引擎
@interface FlutterEngineGroup ()
@property(nonatomic, copy) NSString* name;
@property(nonatomic, retain) NSMutableArray<NSValue*>* engines;
// 目标来指定进口文件和 Assets
@property(nonatomic, retain) FlutterDartProject* project;
@end@implementation FlutterEngineGroup {
 int _enginesCreatedCount;
}
​
/// 创立 FlutterEngineGroup ,内部初始化数组
- (instancetype)initWithName:(NSString*)name project:(nullable FlutterDartProject*)project {
 self = [super init];
 if (self) {
  _name = [name copy];
  _engines = [[NSMutableArray<NSValue*> alloc] init];
  _project = [project retain];
  }
 return self;
}
...

创立 FlutterEngine

1、makeEngineWithOptions

  • 1、引擎创立逻辑,当引擎池没有引擎时,经过makeEngine创立一个新引擎。

  • 2、假如引擎池里有引擎,则回来当时引擎,而且经过 spawnWithEntrypoint 办法设置相同的进口点、dart 库途径、路由、进口参数。

    • a、FlutterEngineGroup 是怎样做到部分的引擎资源共享呢?要点在于 spawnWithEntrypoint 内部完成,后面咱们再说~
  • 3、比如: let newEngine = appDelegate.engines.makeEngine(with: options)

  • 4、makeEngineWithOptions 源码如下:

/// 创立并回来引擎
- (FlutterEngine*)makeEngineWithOptions:(nullable FlutterEngineGroupOptions*)options {
 NSString* entrypoint = options.entrypoint;
 NSString* libraryURI = options.libraryURI;
 NSString* initialRoute = options.initialRoute;
 NSArray<NSString*>* entrypointArgs = options.entrypointArgs;
​
 FlutterEngine* engine;
 // 当引擎没有创立过,那么就创立一个新的引擎
 if (self.engines.count <= 0) {
  engine = [self makeEngine];
   [engine runWithEntrypoint:entrypoint
          libraryURI:libraryURI
         initialRoute:initialRoute
        entrypointArgs:entrypointArgs];
  } else {
  // 已经存在引擎,则获取当时引擎,并经过 spawnWithEntrypoint 办法做一系列操作
  FlutterEngine* spawner = (FlutterEngine*)[self.engines[0] pointerValue];
  engine = [spawner spawnWithEntrypoint:entrypoint
                libraryURI:libraryURI
               initialRoute:initialRoute
              entrypointArgs:entrypointArgs];
  }
  [_engines addObject:[NSValue valueWithPointer:engine]];
​
 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  [center addObserver:self
       selector:@selector(onEngineWillBeDealloced:)
         name:kFlutterEngineWillDealloc
        object:engine];
​
 return engine;
}
​

2、makeEngine

  • makeEngine 的源码
// @property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
// @property(nonatomic, readonly) NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* registrars;
​
​
/// 创立新的引擎
- (FlutterEngine*)makeEngine {
 NSString* engineName = [NSString stringWithFormat:@"%@.%d", self.name, ++_enginesCreatedCount];
 FlutterEngine* result = [[FlutterEngine alloc] initWithName:engineName project:self.project];
 return [result autorelease];
}
​
/// 完成逻辑
- (instancetype)initWithName:(NSString*)labelPrefix
           project:(FlutterDartProject*)project
   allowHeadlessExecution:(BOOL)allowHeadlessExecution
     restorationEnabled:(BOOL)restorationEnabled {
 self = [super init];
 NSAssert(self, @"Super init cannot be nil");
 NSAssert(labelPrefix, @"labelPrefix is required");_restorationEnabled = restorationEnabled;
 _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]);
  }
​
 if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
  NSLog(
    @"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
    @"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
    @"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
    @"profile and release mode apps can be launched from the home screen.");
   [self release];
  return nil;
  }
​
 _pluginPublications = [[NSMutableDictionary alloc] init];
 _registrars = [[NSMutableDictionary alloc] init];
  [self recreatePlatformViewController];_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
 _textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
 _connections.reset(new flutter::ConnectionCollection());
​
 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  [center addObserver:self
       selector:@selector(onMemoryWarning:)
         name:UIApplicationDidReceiveMemoryWarningNotification
        object:nil];[center addObserver:self
       selector:@selector(applicationWillEnterForeground:)
         name:UIApplicationWillEnterForegroundNotification
        object:nil];[center addObserver:self
       selector:@selector(applicationDidEnterBackground:)
         name:UIApplicationDidEnterBackgroundNotification
        object:nil];[center addObserver:self
       selector:@selector(onLocaleUpdated:)
         name:NSCurrentLocaleDidChangeNotification
        object:nil];
​
 return self;
}

a、初始化 FlutterDartProject 目标

  • 1、用于 Flutter 引擎中运转 Dart 代码。Flutter 引擎经过 FlutterDartProject 与 Dart 代码进行交互,比如发动 Dart VM,加载 Dart 库,履行 Dart 函数等等。在 Flutter 运用程序中,通常会创立一个 FlutterDartProject 实例用于办理 Dart 代码的履行。
  • 2、初始化 _settings ,从指定的 bundle 中读取 FlutterSettings 装备信息,并回来一个 FlutterSettings 目标。这个目标包括了运用的各种装备信息,例如引擎的版本、是否启用 Dart 开发者东西、是否支持热重载等。
struct Settings {
  Settings();
   ...
  // Enable the Impeller renderer on supported platforms. Ignored if Impeller is
  // not supported on the platform.
  bool enable_impeller = false;
}
​
- (instancetype)init {
 return [self initWithPrecompiledDartBundle:nil];
}
​
- (instancetype)initWithPrecompiledDartBundle:(nullable NSBundle*)bundle {
 self = [super init];
​
 if (self) {
  _settings = FLTDefaultSettingsForBundle(bundle);
  }
​
 return self;
}
​
flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle) {
  auto command_line = flutter::CommandLineFromNSProcessInfo();
​
  // Precedence:
  // 1. Settings from the specified NSBundle.
  // 2. Settings passed explicitly via command-line arguments.
  // 3. Settings from the NSBundle with the default bundle ID.
  // 4. Settings from the main NSBundle and default values.
​
  NSBundle* mainBundle = [NSBundle mainBundle];
  NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterViewController class]];
​
  bool hasExplicitBundle = bundle != nil;
  if (bundle == nil) {
    bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]];
   }
  if (bundle == nil) {
    bundle = mainBundle;
   }
​
  auto settings = flutter::SettingsFromCommandLine(command_line);settings.task_observer_add = [](intptr_t key, const fml::closure& callback) {
    fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback);
   };settings.task_observer_remove = [](intptr_t key) {
    fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
   };settings.log_message_callback = [](const std::string& tag, const std::string& message) {
    // TODO(cbracken): replace this with os_log-based approach.
    // https://github.com/flutter/flutter/issues/44030
    std::stringstream stream;
    if (!tag.empty()) {
      stream << tag << ": ";
     }
    stream << message;
    std::string log = stream.str();
    syslog(LOG_ALERT, "%.*s", (int)log.size(), log.c_str());
   };
​
  // The command line arguments may not always be complete. If they aren't, attempt to fill in
  // defaults.
​
  // Flutter ships the ICU data file in the bundle of the engine. Look for it there.
  if (settings.icu_data_path.empty()) {
    NSString* icuDataPath = [engineBundle pathForResource:@"icudtl" ofType:@"dat"];
    if (icuDataPath.length > 0) {
      settings.icu_data_path = icuDataPath.UTF8String;
     }
   }
​
  if (flutter::DartVM::IsRunningPrecompiledCode()) {
    if (hasExplicitBundle) {
      NSString* executablePath = bundle.executablePath;
      if ([[NSFileManager defaultManager] fileExistsAtPath:executablePath]) {
        settings.application_library_path.push_back(executablePath.UTF8String);
       }
     }
​
    // No application bundle specified.  Try a known location from the main bundle's Info.plist.
    if (settings.application_library_path.empty()) {
      NSString* libraryName = [mainBundle objectForInfoDictionaryKey:@"FLTLibraryPath"];
      NSString* libraryPath = [mainBundle pathForResource:libraryName ofType:@""];
      if (libraryPath.length > 0) {
        NSString* executablePath = [NSBundle bundleWithPath:libraryPath].executablePath;
        if (executablePath.length > 0) {
          settings.application_library_path.push_back(executablePath.UTF8String);
         }
       }
     }
​
    // In case the application bundle is still not specified, look for the App.framework in the
    // Frameworks directory.
    if (settings.application_library_path.empty()) {
      NSString* applicationFrameworkPath = [mainBundle pathForResource:@"Frameworks/App.framework"
                   ofType:@""];
      if (applicationFrameworkPath.length > 0) {
        NSString* executablePath =
           [NSBundle bundleWithPath:applicationFrameworkPath].executablePath;
        if (executablePath.length > 0) {
          settings.application_library_path.push_back(executablePath.UTF8String);
         }
       }
     }
   }
​
  // Checks to see if the flutter assets directory is already present.
  if (settings.assets_path.empty()) {
    NSString* assetsName = [FlutterDartProject flutterAssetsName:bundle];
    NSString* assetsPath = [bundle pathForResource:assetsName ofType:@""];
​
    if (assetsPath.length == 0) {
      assetsPath = [mainBundle pathForResource:assetsName ofType:@""];
     }
​
    if (assetsPath.length == 0) {
      NSLog(@"Failed to find assets path for "%@"", assetsName);
     } else {
      settings.assets_path = assetsPath.UTF8String;
​
      // Check if there is an application kernel snapshot in the assets directory we could
      // potentially use.  Looking for the snapshot makes sense only if we have a VM that can use
      // it.
      if (!flutter::DartVM::IsRunningPrecompiledCode()) {
        NSURL* applicationKernelSnapshotURL =
           [NSURL URLWithString:@(kApplicationKernelSnapshotFileName)
 relativeToURL:[NSURL fileURLWithPath:assetsPath]];
        if ([[NSFileManager defaultManager] fileExistsAtPath:applicationKernelSnapshotURL.path]) {
          settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String;
         } else {
          NSLog(@"Failed to find snapshot: %@", applicationKernelSnapshotURL.path);
         }
       }
     }
   }
​
  // Domain network configuration
  // Disabled in https://github.com/flutter/flutter/issues/72723.
  // Re-enable in https://github.com/flutter/flutter/issues/54448.
  settings.may_insecurely_connect_to_all_domains = true;
  settings.domain_network_policy = "";
​
  // Whether to enable Impeller.
  NSNumber* nsEnableWideGamut = [mainBundle objectForInfoDictionaryKey:@"FLTEnableWideGamut"];
  // TODO(gaaclarke): Make this value `on` by default (pending memory audit).
  BOOL enableWideGamut = nsEnableWideGamut ? nsEnableWideGamut.boolValue : NO;
  settings.enable_wide_gamut = enableWideGamut;
​
  // Whether to enable Impeller.
  NSNumber* enableImpeller = [mainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"];
  // Change the default only if the option is present.
  if (enableImpeller != nil) {
    settings.enable_impeller = enableImpeller.boolValue;
   }
​
  NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTTraceSystrace"];
  // Change the default only if the option is present.
  if (enableTraceSystrace != nil) {
    settings.trace_systrace = enableTraceSystrace.boolValue;
   }
​
  NSNumber* enableDartProfiling = [mainBundle objectForInfoDictionaryKey:@"FLTEnableDartProfiling"];
  // Change the default only if the option is present.
  if (enableDartProfiling != nil) {
    settings.enable_dart_profiling = enableDartProfiling.boolValue;
   }
​
  // Leak Dart VM settings, set whether leave or clean up the VM after the last shell shuts down.
  NSNumber* leakDartVM = [mainBundle objectForInfoDictionaryKey:@"FLTLeakDartVM"];
  // It will change the default leak_vm value in settings only if the key exists.
  if (leakDartVM != nil) {
    settings.leak_vm = leakDartVM.boolValue;
   }
​
  #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
  // There are no ownership concerns here as all mappings are owned by the
  // embedder and not the engine.
  auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
    return [mapping, size]() { return std::make_unique<fml::NonOwnedMapping>(mapping, size); };
   };settings.dart_library_sources_kernel =
    make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
  #endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
​
  // If we even support setting this e.g. from the command line or the plist,
  // we should let the user override it.
  // Otherwise, we want to set this to a value that will avoid having the OS
  // kill us. On most iOS devices, that happens somewhere near half
  // the available memory.
  // The VM expects this value to be in megabytes.
  if (settings.old_gen_heap_size <= 0) {
    settings.old_gen_heap_size = std::round([NSProcessInfo processInfo].physicalMemory * .48 /
                    flutter::kMegaByteSizeInBytes);
   }
​
  // This is the formula Android uses.
  // https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45
  CGFloat scale = [UIScreen mainScreen].scale;
  CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width * scale;
  CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height * scale;
  settings.resource_cache_max_bytes_threshold = screenWidth * screenHeight * 12 * 4;
​
  return settings;
}
​
Flutter 3.9.0 版才默许烘托引擎 skia or impeller ? 我能够修改关闭默许 skia 烘托形式吗? 以及其他默许装备调整吗?咱们看看 flutter::Settings 默许设置的源码。
  • 从源码读到发现 mainBundle key FLTEnableImpeller 是设置 impeller 烘托形式。截图默许设置 YES,所以咱们也能够关闭 impeller 选用 skia 烘托引擎~

FlutterEngineGroup 引擎启动流程(iOS)

  • 1、Flutter 3.9.0 版本中发现,默许Flutter Engine中资源缓存所占用的最大内存巨细其实是按屏幕巨细动态调整的~很神奇吧~~
  • 2、因此咱们阅览源码去发现 Flutter 引擎一些默许装备~

b、初始化插件表 _pluginPublications 与 _registrars

  • 1、_pluginPublications:每逢一个插件在 FlutterEngine 中注册时, key 对应的 value 会被设置 [NSNull null],并将一个 Flutter 插件注册到 registrar 中,发布到 Flutter 插件库中。
  • 2、_registrars: 办理插件的注册表,存储的是 FlutterPluginRegistrar 插件实例目标。
  • 3、源码能够查看注册插件的逻辑如下
- (void)publish:(NSObject*)value {
 _flutterEngine.pluginPublications[_pluginKey] = value;
}
...  
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
 NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
 self.pluginPublications[pluginKey] = [NSNull null];
 FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
                                  flutterEngine:self];
 self.registrars[pluginKey] = result;
 return [result autorelease];
}
  • 例如注册 dart-native 插件 ,registry 是当时引擎 Engine 调用上述的 registrarForPlugin 注册 DartNativePlugin 插件。
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
  [DartNativePlugin registerWithRegistrar:[registry registrarForPlugin:@"DartNativePlugin"]];
}

c、recreatePlatformViewController

  • 1、设置烘托 API 为 软件烘托形式
  • 2、重新创立并装备与 Flutter 引擎相关的平台视图操控器 FlutterPlatformViewsController
- (void)recreatePlatformViewController {
 _renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering);
 _platformViewsController.reset(new flutter::FlutterPlatformViewsController());
}

d、初始化 Flutter 与原生宿主通讯目标 _binaryMessenger && _textureRegistry

  • 1、FlutterBinaryMessengerRelay 是 Flutter 引擎内部通讯的消息传递机制,它允许 Flutter 插件和宿主运用之间彼此通讯。
  • 2、FlutterTextureRegistryRelay 则是担任办理 Flutter 引擎中所有纹路的注册表。Flutter 引擎中的纹路用于在 Flutter 中制作图画,比如从原生平台传递到 Flutter 中的图画或视频等资源。
  • 3、这两行代码是在初始化 FlutterEngine 目标时创立了这两个要害的目标,保证了后续 Flutter 插件和 Flutter 引擎之间的通讯和纹路办理能够正常进行。
  • 4、_connections.reset(new flutter::ConnectionCollection()) 初始化连接集合,以便在 Flutter Engine 中创立和办理通讯通道。
_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
_textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
_connections.reset(new flutter::ConnectionCollection());
Flutter 引擎完成原生与 Flutter 通讯协议 FlutterBinaryMessenger
  • 原生经过注册的 FlutterMethodChannel 的 _messenger (FlutterBinaryMessenger)目标给 Flutter 端传消息调用 sendOnChannel:message 办法
/// 给 Flutter 发消息
- (void)invokeMethod:(NSString*)method arguments:(id)arguments {
  FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:method
                            arguments:arguments];
  NSData* message = [_codec encodeMethodCall:methodCall];
   [_messenger sendOnChannel:_name message:message];
}
​
/// 承受 Flutter 端的消息
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
  if (!handler) {
    if (_connection > 0) {
       [_messenger cleanUpConnection:_connection];
      _connection = 0;
     } else {
       [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
     }
    return;
   }
  // Make sure the block captures the codec, not self.
  // `self` might be released before the block, so the block needs to retain the codec to
  // make sure it is not released with `self`
  NSObject<FlutterMethodCodec>* codec = _codec;
  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
    FlutterMethodCall* call = [codec decodeMethodCall:message];
    handler(call, ^(id result) {
      if (result == FlutterMethodNotImplemented) {
        callback(nil);
       } else if ([result isKindOfClass:[FlutterError class]]) {
        callback([codec encodeErrorEnvelope:(FlutterError*)result]);
       } else {
        callback([codec encodeSuccessEnvelope:result]);
       }
     });
   };
  _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
}

f、注册 App 生命相关通知

 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  [center addObserver:self
       selector:@selector(onMemoryWarning:)
         name:UIApplicationDidReceiveMemoryWarningNotification
        object:nil];
​
  [center addObserver:self
       selector:@selector(applicationWillEnterForeground:)
         name:UIApplicationWillEnterForegroundNotification
        object:nil];
​
  [center addObserver:self
       selector:@selector(applicationDidEnterBackground:)
         name:UIApplicationDidEnterBackgroundNotification
        object:nil];
​
  [center addObserver:self
       selector:@selector(onLocaleUpdated:)
         name:NSCurrentLocaleDidChangeNotification
        object:nil];

3、runWithEntrypoint:

  • 1、这段代码完成了创立 Flutter Shell 的功用。Shell 是 Flutter 的中心部分之一,用于发动 Dart 代码和 Flutter 运用程序,然后展示 Flutter UI。Shell 是一个 C++ 运用程序,它与 Dart 代码之间运用 Embedder API 进行通讯。在 iOS 平台上,Flutter 的 Shell 是由 Objective-C 代码创立的,与宿主运用程序交互。FlutterEngine 类是与 Shell 交互的首要接口。

  • 2、Shell 的效果?

    • a、Shell 是 Flutter 的底层框架,它供给了一个跨平台的烘托引擎、视图体系和一套根底库。Shell 是构建 Flutter 运用程序的根底,它将运用程序逻辑和 Flutter 引擎的交互封装在一起。
    • b、Flutter 运用程序通常包括一个或多个 Shell,每个 Shell 包括一个烘托线程和一个 Dart 履行上下文。Shell 接收来自 Dart 代码的指令,并将其翻译成图形、动画和其他视觉效果,以及响运用户的输入事件。Shell 还供给了对 Flutter 引擎的拜访,使开发者能够装备引擎和拜访它的功用。
/// 创立 Flutter Shell 并发动引擎
- (BOOL)runWithEntrypoint:(NSString*)entrypoint
        libraryURI:(NSString*)libraryURI
       initialRoute:(NSString*)initialRoute
      entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
 if ([self createShell:entrypoint libraryURI:libraryURI initialRoute:initialRoute]) {
   [self launchEngine:entrypoint libraryURI:libraryURI entrypointArgs:entrypointArgs];
  }
 return _shell != nullptr;
}

a、createShell

  • 这段代码完成了创立 Flutter Shell 的功用。Shell 是 Flutter 的中心部分之一,用于发动 Dart 代码和 Flutter 运用程序,然后展示 Flutter UI。Shell 是一个 C++ 运用程序,它与 Dart 代码之间运用 Embedder API 进行通讯。在 iOS 平台上,Flutter 的 Shell 是由 Objective-C 代码创立的,与宿主运用程序交互。FlutterEngine 类是与 Shell 交互的首要接口。

  • 具体过程如下:

  • 1、首先,该函数查看 Shell 是否已经创立。假如已经创立,函数直接回来。

  • 2、接下来,该函数将传入的 entrypoint、libraryURI 和 initialRoute 参数保存到目标特点中。entrypoint 表明 Dart 代码的进口点,libraryURI 表明运用程序的 Dart 库文件途径,initialRoute 表明运用程序的初始路由。假如 initialRoute 参数为空,该函数尝试从 Dart 装备中获取默许的路由。

  • 3、然后,该函数设置了 FlutterView.forceSoftwareRendering 特点。该特点用于操控是否启用软件烘托,依据 Dart 装备中的 enable_software_rendering 参数来决议是否启用。

  • 4、接着,该函数创立了一个 Flutter 的 PlatformData 目标。PlatformData 包括了许多平台相关的信息,如窗口巨细、文本方向、缩放份额等。在 iOS 平台上,PlatformData 包括了平台视图的巨细和方位信息。

  • 5、然后,该函数设置了进口点信息,并生成一个线程标签用于标识该 FlutterEngine 的线程。接着,函数创立了一个 ThreadHost 目标,该目标表明线程的主机,并运用 makeThreadHost 函数创立了三个线程:UI 线程、IO 线程和 Raster 线程。这些线程将在后续的 Shell 创立过程中运用。

  • 6、然后,该函数创立了两个回调函数 on_create_platform_view 和 on_create_rasterizer。on_create_platform_view 回调函数用于创立平台视图,而 on_create_rasterizer 回调函数用于创立光栅化器。这些回调函数将在后续的 Shell 创立过程中运用。

  • 7、接着,该函数创立了一个 TaskRunners 目标,该目标封装了线程标签、平台使命运转器、光栅化使命运转器、UI 使命运转器和 IO 使命运转器。这些使命运转器将在后续的 Shell 创立过程中运用。

  • 8、接下来,该函数查看运用程序是否处于后台状况。假如运用程序处于后台状况,则禁用 GPU,否则启用 GPU。禁用 GPU 能够减少电量消耗和热量,然后延伸电池寿命。

  • 9、然后,该函数调用 Shell::Create 函数创立 Shell。Shell::Create 函数是一个同步函数,它会阻塞当时线程,直到 Shell 创立完成

  • 10、最后,调用 setupShell 设置 Shell , 而且判别假如是 isProfilerEnabled 形式下,敞开功用剖析

// 创立 Flutter Shell
- (BOOL)createShell:(NSString*)entrypoint
     libraryURI:(NSString*)libraryURI
    initialRoute:(NSString*)initialRoute {
 if (_shell != nullptr) {
  FML_LOG(WARNING) << "This FlutterEngine was already invoked.";
  return NO;
  }
​
 self.initialRoute = initialRoute;
​
 auto settings = [_dartProject.get() settings];
 if (initialRoute != nil) {
  self.initialRoute = initialRoute;
  } else if (settings.route.empty() == false) {
  self.initialRoute = [NSString stringWithCString:settings.route.c_str()
                      encoding:NSUTF8StringEncoding];
  }
​
 FlutterView.forceSoftwareRendering = settings.enable_software_rendering;
​
 auto platformData = [_dartProject.get() defaultPlatformData];
​
 SetEntryPoint(&settings, entrypoint, libraryURI);
​
 NSString* threadLabel = [FlutterEngine generateThreadLabel:_labelPrefix];
 _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.
 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); };
​
 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
  );_isGpuDisabled =
    [UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
 // Create the shell. This is a blocking operation.
 std::unique_ptr<flutter::Shell> shell = flutter::Shell::Create(
   /*platform_data=*/platformData,
   /*task_runners=*/task_runners,
   /*settings=*/settings,
   /*on_create_platform_view=*/on_create_platform_view,
   /*on_create_rasterizer=*/on_create_rasterizer,
   /*is_gpu_disabled=*/_isGpuDisabled);
​
 if (shell == nullptr) {
  FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: "
          << entrypoint.UTF8String;
  } else {
   [self setupShell:std::move(shell)
    withVMServicePublication:settings.enable_vm_service_publication];
  if ([FlutterEngine isProfilerEnabled]) {
    [self startProfiler];
   }
  }
​
 return _shell != nullptr;
}
​

b、launchEngine

  • 装备发动 Dart 运用程序引擎的一些列装备包括

    • 1、代码库途径
    • 2、进口点(entrypoint)
    • 3、包括可履行文件的途径
    • 4、环境变量
    • 5、发动参数等…
// 发动引擎
- (void)launchEngine:(NSString*)entrypoint
libraryURI:(NSString*)libraryOrNil
entrypointArgs:(NSArray<NSString*>*)entrypointArgs {
  // Launch the Dart application with the inferred run configuration.
  self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
                    libraryOrNil:libraryOrNil
                    entrypointArgs:entrypointArgs]);
}
​
- (flutter::RunConfiguration)runConfigurationForEntrypoint:(nullable NSString*)entrypointOrNil
libraryOrNil:(nullable NSString*)dartLibraryOrNil
entrypointArgs:
(nullable NSArray<NSString*>*)entrypointArgs {
  auto config = flutter::RunConfiguration::InferFromSettings(_settings);
  if (dartLibraryOrNil && entrypointOrNil) {
    config.SetEntrypointAndLibrary(std::string([entrypointOrNil UTF8String]),
                std::string([dartLibraryOrNil UTF8String]));
​
   } else if (entrypointOrNil) {
    config.SetEntrypoint(std::string([entrypointOrNil UTF8String]));
   }
​
  if (entrypointArgs.count) {
    std::vector<std::string> cppEntrypointArgs;
    for (NSString* arg in entrypointArgs) {
      cppEntrypointArgs.push_back(std::string([arg UTF8String]));
     }
    config.SetEntrypointArgs(std::move(cppEntrypointArgs));
   }
​
  return config;
}

4、spawnWithEntrypoint

  • 当引擎组里 self.engines.count > 0 的时候,则经过下面源码创立一个引擎,一起它会共享第一个引擎的部分资源~

  • 该办法用于创立一个新的 FlutterEngine 实例,该实例包括一个 FlutterShell 实例和一个 FlutterDartProject 实例,而且会经过调用 FlutterShell 实例的 Spawn 办法来生成一个新的 Shell 实例,这个新的 Shell 实例能够用于操控新的 Flutter 引擎实例。

  • 具体过程如下:

  • 1.依据 _dartProject 获取运转装备 configuration。

  • 2.从当时 FlutterShell 实例 _shell 中获取平台视图,并经过 on_create_platform_view 办法创立一个新的 PlatformViewIOS 实例,一起经过 on_create_rasterizer 办法创立一个新的 Rasterizer 实例。

  • 3.调用 _shell 的 Spawn 办法创立一个新的 Shell 实例,并将过程2中创立的 PlatformViewIOS 和 Rasterizer 实例传递给 Spawn 办法。

  • 4.创立一个新的 FlutterEngine 实例 result,将 threadHost、 profiler、_profiler_metrics 和 _isGpuDisabled 特点复制到 result 中。

  • 5.调用 result 的 setupShell:withVMServicePublication: 办法,将过程3中创立的 Shell 实例作为参数传递给 setupShell 办法,一起传递一个 NO 值,表明不启用 VM service publication。

  • 6.回来 result。

总归,这段代码是用于创立一个新的 Flutter 引擎实例的,并经过调用 FlutterShell 实例的 Spawn 办法来生成一个新的 Shell 实例,这个新的 Shell 实例能够用于操控新的 Flutter 引擎实例。

- (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint
              libraryURI:(/*nullable*/ NSString*)libraryURI
             initialRoute:(/*nullable*/ NSString*)initialRoute
            entrypointArgs:(/*nullable*/ NSArray<NSString*>*)entrypointArgs {
 NSAssert(_shell, @"Spawning from an engine without a shell (possibly not run).");
 FlutterEngine* result = [[FlutterEngine alloc] initWithName:_labelPrefix
                           project:_dartProject.get()
                    allowHeadlessExecution:_allowHeadlessExecution];
 flutter::RunConfiguration configuration =
    [_dartProject.get() runConfigurationForEntrypoint:entrypoint
                      libraryOrNil:libraryURI
                     entrypointArgs:entrypointArgs];
​
 fml::WeakPtr<flutter::PlatformView> platform_view = _shell->GetPlatformView();
 FML_DCHECK(platform_view);
 // Static-cast safe since this class always creates PlatformViewIOS instances.
 flutter::PlatformViewIOS* ios_platform_view =
   static_cast<flutter::PlatformViewIOS*>(platform_view.get());
 std::shared_ptr<flutter::IOSContext> context = ios_platform_view->GetIosContext();
 FML_DCHECK(context);
​
 // Lambda captures by pointers to ObjC objects are fine here because the
 // create call is synchronous.
 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
    [result, context](flutter::Shell& shell) {
     [result recreatePlatformViewController];
    return std::make_unique<flutter::PlatformViewIOS>(
      shell, context, result->_platformViewsController, shell.GetTaskRunners());
    };
​
 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
    [](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
​
 std::string cppInitialRoute;
 if (initialRoute) {
  cppInitialRoute = [initialRoute UTF8String];
  }
​
 std::unique_ptr<flutter::Shell> shell = _shell->Spawn(
   std::move(configuration), cppInitialRoute, on_create_platform_view, on_create_rasterizer);
​
 result->_threadHost = _threadHost;
 result->_profiler = _profiler;
 result->_profiler_metrics = _profiler_metrics;
 result->_isGpuDisabled = _isGpuDisabled;
  [result setupShell:std::move(shell) withVMServicePublication:NO];
 return [result autorelease];
}
  • 经过 spawnWithEntrypoint 源码咱们知道了选用 Group 办理多引擎,为什么能大大下降内存原因:因为多个引擎的共用的是同一个 _shell 部分资源~

题外话

  • Flutter 网络请求是在哪个线程中履行的吗?会阻塞引发 App 卡顿吗?

FlutterEngineGroup 引擎启动流程(iOS)

参阅

  • 【官方文档】在 iOS 运用中增加 Flutter 页面
  • 【官方文档】FlutterEngine