Flutter引擎源碼解讀-Flutter是如何在iOS上運(yùn)行起來的

摘要

本文主要是針對 Flutter 在 iOS 上是如何運(yùn)行起來的源碼進(jìn)行串聯(lián)筷黔,總結(jié)大致的運(yùn)行流程业汰。

涉及到的關(guān)鍵類有以下幾個(gè):

  • FlutterViewController
  • FlutterView
  • FlutterEngine
  • DartIsolate

FlutterViewController

Flutter 嵌入原生應(yīng)用必須有個(gè)載體,從這個(gè)點(diǎn)入手,在 Flutter Engine 源碼中的 API 的入口點(diǎn)是 FlutterViewController,對其頭文件源碼做精簡番官,大致如下

@interface FlutterViewController : UIViewController <FlutterTextureRegistry, FlutterPluginRegistry>

- (instancetype)initWithEngine:(FlutterEngine*)engine
                       nibName:(nullable NSString*)nibName
                        bundle:(nullable NSBundle*)nibBundle NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithProject:(nullable FlutterDartProject*)project
                        nibName:(nullable NSString*)nibName
                         bundle:(nullable NSBundle*)nibBundle NS_DESIGNATED_INITIALIZER;

- (void)handleStatusBarTouches:(UIEvent*)event;

- (void)setFlutterViewDidRenderCallback:(void (^)(void))callback;

- (NSString*)lookupKeyForAsset:(NSString*)asset;

- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package;

- (void)setInitialRoute:(NSString*)route;

- (void)popRoute;

- (void)pushRoute:(NSString*)route;

- (id<FlutterPluginRegistry>)pluginRegistry;

@property(nonatomic, readonly, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;

@property(strong, nonatomic) UIView* splashScreenView;

- (BOOL)loadDefaultSplashScreenView;

@property(nonatomic, getter=isViewOpaque) BOOL viewOpaque;

@property(weak, nonatomic, readonly) FlutterEngine* engine;

@property(nonatomic, readonly) NSObject<FlutterBinaryMessenger>* binaryMessenger;

@end

FlutterViewController 的構(gòu)造函數(shù)

FlutterViewController 有兩個(gè)構(gòu)造函數(shù),本質(zhì)上是一樣的钢属,第一個(gè)構(gòu)造函數(shù)是谷歌為了在存在多個(gè) FlutterViewController 的場景下為了讓使用者能復(fù)用 FlutterEngine 而開放的徘熔。

- (instancetype)initWithEngine:(FlutterEngine*)engine
                       nibName:(nullable NSString*)nibName
                        bundle:(nullable NSBundle*)nibBundle {
  NSAssert(engine != nil, @"Engine is required");
  self = [super initWithNibName:nibName bundle:nibBundle];
  if (self) {
    _viewOpaque = YES;
    _engine.reset([engine retain]);
    _engineNeedsLaunch = NO;
    _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
    _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
    _ongoingTouches = [[NSMutableSet alloc] init];

    [self performCommonViewControllerInitialization];
    [engine setViewController:self];
  }

  return self;
}

- (instancetype)initWithProject:(nullable FlutterDartProject*)project
                        nibName:(nullable NSString*)nibName
                         bundle:(nullable NSBundle*)nibBundle {
  self = [super initWithNibName:nibName bundle:nibBundle];
  if (self) {
    _viewOpaque = YES;
    _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
    _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter"
                                              project:project
                               allowHeadlessExecution:NO]);
    _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
    [_engine.get() createShell:nil libraryURI:nil];
    _engineNeedsLaunch = YES;
    _ongoingTouches = [[NSMutableSet alloc] init];
    [self loadDefaultSplashScreenView];
    [self performCommonViewControllerInitialization];
  }

  return self;
}

在構(gòu)造函數(shù)中主要做了這么幾件事情:

  • 初始化或者替換當(dāng)前的 FlutterEngine

  • 初始化 FlutterView

  • 初始化正在發(fā)生的手勢集合

  • 加載閃屏頁,傳入 FlutterEngine 的構(gòu)造函數(shù)沒有這項(xiàng)淆党,應(yīng)該是考慮了多 FlutterViewController 的場景下不好頻繁加載閃屏頁

  • 設(shè)置 UIInterfaceOrientationMaskUIStatusBarStyle

  • 添加一系列的通知酷师,包括 Application 的生命周期,鍵盤事件染乌,Accessibility的事件等

  • FlutterViewController 設(shè)置給 FlutterEngine

第二個(gè)構(gòu)造函數(shù)中還多了這行代碼山孔,第一個(gè)構(gòu)造函數(shù)把這個(gè)調(diào)用延后了而已

    [_engine.get() createShell:nil libraryURI:nil];

FlutterViewController 的 loadView

loadView 函數(shù)中,設(shè)置了 FlutterViewControllerview慕匠,并判斷是否需要加載閃屏頁饱须,可以通過重寫 splashScreenView 的 get 方法返回 nil 的方式徹底不加載閃屏頁

- (void)loadView {
  self.view = _flutterView.get();
  self.view.multipleTouchEnabled = YES;
  self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

  [self installSplashScreenViewIfNecessary];
}

FlutterViewController 對 Navigator 的操作

FlutterViewController 提供了三個(gè)接口允許我們在原生端對 dart 的 Navigator 直接進(jìn)行操作

- (void)setInitialRoute:(NSString*)route {
  [[_engine.get() navigationChannel] invokeMethod:@"setInitialRoute" arguments:route];
}

- (void)popRoute {
  [[_engine.get() navigationChannel] invokeMethod:@"popRoute" arguments:nil];
}

- (void)pushRoute:(NSString*)route {
  [[_engine.get() navigationChannel] invokeMethod:@"pushRoute" arguments:route];
}

setInitialRoute

setInitialRoute 在 iOS 端通過 navigationChannel 來告訴 dart 具體的 initialRoute,這個(gè)過程略微特殊台谊,并不會(huì)在 dart 端直接接收 channel 信息,
而是在引擎層面做了處理譬挚,web_ui 不在本文的解析范疇锅铅,這里直接洗跟原生相關(guān)的點(diǎn)

setInitialRoute 設(shè)置流程如下:

DispatchPlatformMessage -> HandleNavigationPlatformMessage -> initial_route_

void Engine::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) {
  if (message->channel() == kLifecycleChannel) {
    if (HandleLifecyclePlatformMessage(message.get()))
      return;
  } else if (message->channel() == kLocalizationChannel) {
    if (HandleLocalizationPlatformMessage(message.get()))
      return;
  } else if (message->channel() == kSettingsChannel) {
    HandleSettingsPlatformMessage(message.get());
    return;
  }

  if (runtime_controller_->IsRootIsolateRunning() &&
      runtime_controller_->DispatchPlatformMessage(std::move(message))) {
    return;
  }

  // If there's no runtime_, we may still need to set the initial route.
  if (message->channel() == kNavigationChannel) {
    HandleNavigationPlatformMessage(std::move(message));
    return;
  }

  FML_DLOG(WARNING) << "Dropping platform message on channel: "
                    << message->channel();
}
bool Engine::HandleNavigationPlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  const auto& data = message->data();

  rapidjson::Document document;
  document.Parse(reinterpret_cast<const char*>(data.data()), data.size());
  if (document.HasParseError() || !document.IsObject())
    return false;
  auto root = document.GetObject();
  auto method = root.FindMember("method");
  if (method->value != "setInitialRoute")
    return false;
  auto route = root.FindMember("args");
  initial_route_ = std::move(route->value.GetString());
  return true;
}

setInitialRoute 最終在 HandleNavigationPlatformMessage 函數(shù)中直接被賦值給 initial_route_

setInitialRoute 讀取流程如下:

Window.defaultRouteName -> DefaultRouteName -> Engine::DefaultRouteName -> initial_route_

可以看到减宣,關(guān)鍵字 native盐须,這是 dart 為了方便綁定 C/C++ 導(dǎo)出方法而添加的關(guān)鍵字,對應(yīng)的 key 是 Window_defaultRouteName

class Window {
  String get defaultRouteName => _defaultRouteName();
  String _defaultRouteName() native 'Window_defaultRouteName';
}

可以找到在引擎層的 flutter 命名空間下漆腌,有下面這個(gè)函數(shù)贼邓,注冊了對應(yīng)的導(dǎo)出函數(shù)阶冈,這里對應(yīng)的是 DefaultRouteName

void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
  natives->Register({
      {"Window_defaultRouteName", DefaultRouteName, 1, true},
      {"Window_scheduleFrame", ScheduleFrame, 1, true},
      {"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
      {"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
      {"Window_render", Render, 2, true},
      {"Window_updateSemantics", UpdateSemantics, 2, true},
      {"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
      {"Window_reportUnhandledException", ReportUnhandledException, 2, true},
      {"Window_setNeedsReportTimings", SetNeedsReportTimings, 2, true},
  });
}
void DefaultRouteName(Dart_NativeArguments args) {
  std::string routeName =
      UIDartState::Current()->window()->client()->DefaultRouteName();
  Dart_SetReturnValue(args, tonic::StdStringToDart(routeName));
}

再往下就是到 engine.cc 文件下面的函數(shù),讀取 initial_route_ 的值

std::string Engine::DefaultRouteName() {
  if (!initial_route_.empty()) {
    return initial_route_;
  }
  return "/";
}

至此塑径,完成了在原生端設(shè)置 defaultRouteName女坑,在 dart 端獲取該值的流程。

pushRoute and popRoute

實(shí)現(xiàn)方式主要還是通過引擎內(nèi)置的 navigationChannel 通知 dart 端统舀,對應(yīng)的在 dart 端 SystemChannels 類中匆骗,存在對應(yīng)的 channel

static const MethodChannel navigation = MethodChannel(
      'flutter/navigation',
      JSONMethodCodec(),
  );

最終處理 pushRoutepopRoute 的邏輯在 WidgetsBinding 類中,主要是以下幾個(gè)函數(shù)

  Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
    switch (methodCall.method) {
      case 'popRoute':
        return handlePopRoute();
      case 'pushRoute':
        return handlePushRoute(methodCall.arguments as String);
    }
    return Future<dynamic>.value();
  }

  Future<void> handlePushRoute(String route) async {
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
      if (await observer.didPushRoute(route))
        return;
    }
  }

  Future<void> handlePopRoute() async {
    for (final WidgetsBindingObserver observer in List<WidgetsBindingObserver>.from(_observers)) {
      if (await observer.didPopRoute())
        return;
    }
    SystemNavigator.pop();
  }

這段代碼表示只有調(diào)用的方法返回 true 時(shí)才中斷誉简,每個(gè) handle 函數(shù)具體的處理邏輯是通過某個(gè) WidgetsBindingObserver 來實(shí)現(xiàn)了碉就,繼續(xù)跟進(jìn)找到如下代碼

class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {

  @override
  Future<bool> didPopRoute() async {
    assert(mounted);
    final NavigatorState navigator = _navigator?.currentState;
    if (navigator == null)
      return false;
    return await navigator.maybePop();
  }

  @override
  Future<bool> didPushRoute(String route) async {
    assert(mounted);
    final NavigatorState navigator = _navigator?.currentState;
    if (navigator == null)
      return false;
    navigator.pushNamed(route);
    return true;
  }
}

handlePopRoute 函數(shù)中,如果沒有任何一個(gè) observer 返回 true闷串,則最終調(diào)用 SystemNavigator.pop(); 來退出應(yīng)用程序

class SystemNavigator {
  static Future<void> pop({bool animated}) async {
    await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop', animated);
  }
}

FlutterView

FlutterView 并沒有太多功能瓮钥,主要是兩點(diǎn):

  • 初始化時(shí)傳入 FlutterViewEngineDelegate
  • 創(chuàng)建 flutter::IOSSurface
@protocol FlutterViewEngineDelegate <NSObject>

- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type
                                  asBase64Encoded:(BOOL)base64Encode;

- (flutter::FlutterPlatformViewsController*)platformViewsController;

@end

@interface FlutterView : UIView

- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate
                          opaque:(BOOL)opaque NS_DESIGNATED_INITIALIZER;
- (std::unique_ptr<flutter::IOSSurface>)createSurface:
    (std::shared_ptr<flutter::IOSGLContext>)context;

@end

takeScreenshot:asBase64Encoded: 應(yīng)該是 FlutterView 渲染的數(shù)據(jù)源,具體參考 drawLayer:inContext: 的源碼

@implementation FlutterView
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
  if (layer != self.layer || context == nullptr) {
    return;
  }

  auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
                              asBase64Encoded:NO];

  if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) {
    return;
  }

  NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
                                length:screenshot.data->size()];

  fml::CFRef<CGDataProviderRef> image_data_provider(
      CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));

  fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());

  fml::CFRef<CGImageRef> image(CGImageCreate(
      screenshot.frame_size.width(),      // size_t width
      screenshot.frame_size.height(),     // size_t height
      8,                                  // size_t bitsPerComponent
      32,                                 // size_t bitsPerPixel,
      4 * screenshot.frame_size.width(),  // size_t bytesPerRow
      colorspace,                         // CGColorSpaceRef space
      static_cast<CGBitmapInfo>(kCGImageAlphaPremultipliedLast |
                                kCGBitmapByteOrder32Big),  // CGBitmapInfo bitmapInfo
      image_data_provider,                                 // CGDataProviderRef provider
      nullptr,                                             // const CGFloat* decode
      false,                                               // bool shouldInterpolate
      kCGRenderingIntentDefault                            // CGColorRenderingIntent intent
      ));

  const CGRect frame_rect =
      CGRectMake(0.0, 0.0, screenshot.frame_size.width(), screenshot.frame_size.height());

  CGContextSaveGState(context);
  CGContextTranslateCTM(context, 0.0, CGBitmapContextGetHeight(context));
  CGContextScaleCTM(context, 1.0, -1.0);
  CGContextDrawImage(context, frame_rect, image);
  CGContextRestoreGState(context);
}
@end

后面我們會(huì)看到 FlutterViewEngineDelegate 實(shí)際上是被 FlutterEngine 實(shí)現(xiàn)了烹吵。

這里不對 IOSSurface 做過多解析骏庸,其是建立在三種 layer 之上的,可以在編譯期選擇使用何種渲染方式

  • 如果是模擬器年叮,則使用正常的 CALayer
  • 使用 Metal 渲染的情形則使用 CAMetalLayer
  • 使用 OpenGL 渲染的情形則使用 CAEAGLLayer
+ (Class)layerClass {
#if TARGET_IPHONE_SIMULATOR
  return [CALayer class];
#else  // TARGET_IPHONE_SIMULATOR
#if FLUTTER_SHELL_ENABLE_METAL
  return [CAMetalLayer class];
#else   // FLUTTER_SHELL_ENABLE_METAL
  return [CAEAGLLayer class];
#endif  //  FLUTTER_SHELL_ENABLE_METAL
#endif  // TARGET_IPHONE_SIMULATOR
}

createSurface 函數(shù)中主要是分別創(chuàng)建三種對應(yīng)的 IOSSurface

CALayer -> IOSSurfaceSoftware
CAEAGLLayer -> IOSSurfaceGL
CAMetalLayer -> IOSSurfaceMetal

再往下的渲染實(shí)際上就要交給 FlutterEngine 自身了具被。

FlutterEngine

FlutterEngine 對外暴露的接口不算多,主要就這么幾點(diǎn)

  • 構(gòu)造函數(shù)只损,initWithName:project:allowHeadlessExecution一姿,allowHeadlessExecution允許初始化引擎時(shí)不強(qiáng)依賴FlutterViewController`
  • 啟動(dòng)引擎,runWithEntrypoint:libraryURI: 可傳入自定義的 entrypoint
  • 釋放資源跃惫,destroyContext
  • 語義樹是否建立叮叹,ensureSemanticsEnabled,關(guān)于語義樹文檔比較少爆存,大概是殘疾人模式下需要用到的東西
  • FlutterViewController 的 get/set
  • 最后是一堆內(nèi)置的 channel

我們主要關(guān)心引擎的構(gòu)造蛉顽、啟動(dòng)、釋放和 FlutterViewController 就差不多了先较,FlutterTextureRegistry, FlutterPluginRegistry 不在本文關(guān)注范圍內(nèi)

@interface FlutterEngine : NSObject <FlutterTextureRegistry, FlutterPluginRegistry>

- (instancetype)initWithName:(NSString*)labelPrefix
                     project:(nullable FlutterDartProject*)project
      allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER;

- (BOOL)runWithEntrypoint:(nullable NSString*)entrypoint libraryURI:(nullable NSString*)uri;

- (void)destroyContext;

- (void)ensureSemanticsEnabled;

@property(nonatomic, weak) FlutterViewController* viewController;

@property(nonatomic, readonly, nullable) FlutterMethodChannel* localizationChannel;

@property(nonatomic, readonly) FlutterMethodChannel* navigationChannel;

@property(nonatomic, readonly) FlutterMethodChannel* platformChannel;

@property(nonatomic, readonly) FlutterMethodChannel* textInputChannel;

@property(nonatomic, readonly) FlutterBasicMessageChannel* lifecycleChannel;

@property(nonatomic, readonly) FlutterBasicMessageChannel* systemChannel;

@property(nonatomic, readonly) FlutterBasicMessageChannel* settingsChannel;

@property(nonatomic, readonly) NSObject<FlutterBinaryMessenger>* binaryMessenger;

@property(nonatomic, readonly, copy, nullable) NSString* isolateId;

@end

FlutterEngine 的構(gòu)造

FlutterEngine在構(gòu)造時(shí)携冤,要關(guān)注的有幾下兩點(diǎn):

  • FlutterDartProject 初始化
  • FlutterPlatformViewsController 的初始化
- (instancetype)initWithName:(NSString*)labelPrefix
                     project:(FlutterDartProject*)project
      allowHeadlessExecution:(BOOL)allowHeadlessExecution {
  self = [super init];
  NSAssert(self, @"Super init cannot be nil");
  NSAssert(labelPrefix, @"labelPrefix is required");

  _allowHeadlessExecution = allowHeadlessExecution;
  _labelPrefix = [labelPrefix copy];

  _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);

  if (project == nil)
    _dartProject.reset([[FlutterDartProject alloc] init]);
  else
    _dartProject.reset([project retain]);

  _pluginPublications = [NSMutableDictionary new];
  _platformViewsController.reset(new flutter::FlutterPlatformViewsController());

  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];

  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  [center addObserver:self
             selector:@selector(onMemoryWarning:)
                 name:UIApplicationDidReceiveMemoryWarningNotification
               object:nil];

  return self;
}

FlutterEngine 的啟動(dòng)

FlutterEngine 層面,需要關(guān)注以下一些類:

  • FlutterDartProject
  • flutter::ThreadHost
  • flutter::Shell
  • FlutterObservatoryPublisher
  • FlutterPlatformViewsController

FlutterEngine 的啟動(dòng)闲勺,主要是兩個(gè)事情

  • createShell
  • launchEngine
- (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
  if ([self createShell:entrypoint libraryURI:libraryURI]) {
    [self launchEngine:entrypoint libraryURI:libraryURI];
  }

  return _shell != nullptr;
}

createShell

createShell 的源碼比較多曾棕,做了下精簡,主要是以下幾點(diǎn):

  • 初始化 MessageLoop
  • 初始化 ThreadHost
  • 設(shè)置 on_create_platform_view 回調(diào)
  • 設(shè)置 on_create_rasterizer 回調(diào)
  • 初始化 flutter::TaskRunners菜循,如果開啟 embedded_views_preview 則 使用當(dāng)前線程的 TaskRunner 作為 gpu 線程的 TaskRunner
  • 創(chuàng)建 shell翘地,最終是啟動(dòng) Isolate
  • 創(chuàng)建 FlutterPlatformViewsController
  • 創(chuàng)建 FlutterObservatoryPublisher
  • 設(shè)置 PlatformView channels
- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {

  // ……

  fml::MessageLoop::EnsureInitializedForCurrentThread();

  _threadHost = {threadLabel.UTF8String, flutter::ThreadHost::Type::UI |
                                         flutter::ThreadHost::Type::GPU |
                                         flutter::ThreadHost::Type::IO};

  flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
      [](flutter::Shell& shell) {
        return std::make_unique<flutter::PlatformViewIOS>(shell, shell.GetTaskRunners());
      };

  flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
      [](flutter::Shell& shell) {
        return std::make_unique<flutter::Rasterizer>(shell, shell.GetTaskRunners());
      };

  if (flutter::IsIosEmbeddedViewsPreviewEnabled()) {
    flutter::TaskRunners task_runners(threadLabel.UTF8String,                          // label
                                      fml::MessageLoop::GetCurrent().GetTaskRunner(),  // platform
                                      fml::MessageLoop::GetCurrent().GetTaskRunner(),  // gpu
                                      _threadHost.ui_thread->GetTaskRunner(),          // ui
                                      _threadHost.io_thread->GetTaskRunner()           // io
    );
    // Create the shell. This is a blocking operation.
    _shell = flutter::Shell::Create(std::move(task_runners),  // task runners
                                    std::move(settings),      // settings
                                    on_create_platform_view,  // platform view creation
                                    on_create_rasterizer      // rasterzier creation
    );
  } else {
    flutter::TaskRunners task_runners(threadLabel.UTF8String,                          // label
                                      fml::MessageLoop::GetCurrent().GetTaskRunner(),  // platform
                                      _threadHost.gpu_thread->GetTaskRunner(),         // gpu
                                      _threadHost.ui_thread->GetTaskRunner(),          // ui
                                      _threadHost.io_thread->GetTaskRunner()           // io
    );
    // Create the shell. This is a blocking operation.
    _shell = flutter::Shell::Create(std::move(task_runners),  // task runners
                                    std::move(settings),      // settings
                                    on_create_platform_view,  // platform view creation
                                    on_create_rasterizer      // rasterzier creation
    );
  }

  if (_shell != nullptr) {
    [self setupChannels];
    if (!_platformViewsController) {
      _platformViewsController.reset(new flutter::FlutterPlatformViewsController());
    }
    _publisher.reset([[FlutterObservatoryPublisher alloc] init]);
    [self maybeSetupPlatformViewChannels];
  }

  return _shell != nullptr;
}

這里可以看到會(huì)啟動(dòng)四個(gè) TaskRunner,分別為 platform,gpu, ui,io,但實(shí)際上并不一定對應(yīng)四個(gè)線程衙耕。

launchEngine

launchEngine 實(shí)際上還是在 shell 上進(jìn)行的操作

- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil {
  // Launch the Dart application with the inferred run configuration.
  self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
                                                            libraryOrNil:libraryOrNil]);
}
void Shell::RunEngine(RunConfiguration run_configuration) {
  RunEngine(std::move(run_configuration), nullptr);
}

void Shell::RunEngine(RunConfiguration run_configuration,
                      std::function<void(Engine::RunStatus)> result_callback) {
  auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(),
                 result_callback](Engine::RunStatus run_result) {
    if (!result_callback) {
      return;
    }
    platform_runner->PostTask(
        [result_callback, run_result]() { result_callback(run_result); });
  };
  FML_DCHECK(is_setup_);
  FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());

  if (!weak_engine_) {
    result(Engine::RunStatus::Failure);
  }
  fml::TaskRunner::RunNowOrPostTask(
      task_runners_.GetUITaskRunner(),
      fml::MakeCopyable(
          [run_configuration = std::move(run_configuration),
           weak_engine = weak_engine_, result]() mutable {
            if (!weak_engine) {
              FML_LOG(ERROR)
                  << "Could not launch engine with configuration - no engine.";
              result(Engine::RunStatus::Failure);
              return;
            }
            auto run_result = weak_engine->Run(std::move(run_configuration));
            if (run_result == flutter::Engine::RunStatus::Failure) {
              FML_LOG(ERROR) << "Could not launch engine with configuration.";
            }
            result(run_result);
          }));
}

再跟下去昧穿,最終會(huì)到[shell > common > engine.cc] 里面的 run 函數(shù),最核心的是這行代碼 PrepareAndLaunchIsolate橙喘,最終整個(gè)流程跑下來就是為了啟動(dòng) Isolate

Engine::RunStatus Engine::Run(RunConfiguration configuration) {
  if (!configuration.IsValid()) {
    FML_LOG(ERROR) << "Engine run configuration was invalid.";
    return RunStatus::Failure;
  }

  auto isolate_launch_status =
      PrepareAndLaunchIsolate(std::move(configuration));
  if (isolate_launch_status == Engine::RunStatus::Failure) {
    FML_LOG(ERROR) << "Engine not prepare and launch isolate.";
    return isolate_launch_status;
  } else if (isolate_launch_status ==
             Engine::RunStatus::FailureAlreadyRunning) {
    return isolate_launch_status;
  }

  std::shared_ptr<DartIsolate> isolate =
      runtime_controller_->GetRootIsolate().lock();

  bool isolate_running =
      isolate && isolate->GetPhase() == DartIsolate::Phase::Running;

  if (isolate_running) {
    tonic::DartState::Scope scope(isolate.get());

    if (settings_.root_isolate_create_callback) {
      settings_.root_isolate_create_callback();
    }

    if (settings_.root_isolate_shutdown_callback) {
      isolate->AddIsolateShutdownCallback(
          settings_.root_isolate_shutdown_callback);
    }

    std::string service_id = isolate->GetServiceId();
    fml::RefPtr<PlatformMessage> service_id_message =
        fml::MakeRefCounted<flutter::PlatformMessage>(
            kIsolateChannel,
            std::vector<uint8_t>(service_id.begin(), service_id.end()),
            nullptr);
    HandlePlatformMessage(service_id_message);
  }

  return isolate_running ? Engine::RunStatus::Success
                         : Engine::RunStatus::Failure;
}

DartIsolate

PrepareAndLaunchIsolate 函數(shù)做下精簡时鸵,剩下兩個(gè)點(diǎn)

  • PrepareIsolate
  • RunFromLibrary
Engine::RunStatus Engine::PrepareAndLaunchIsolate(RunConfiguration configuration) {
  // ……

  if (!isolate_configuration->PrepareIsolate(*isolate)) {
    return RunStatus::Failure;
  }

  if (!isolate->RunFromLibrary(configuration.GetEntrypointLibrary(),
                               configuration.GetEntrypoint(),
                               settings_.dart_entrypoint_args)) {
    return RunStatus::Failure;
  }

  return RunStatus::Success;
}

主要看看 RunFromLibrary 做了什么

  • 查找 entrypoint
  • 調(diào)用 entrypoint 的函數(shù),InvokeMainEntrypoint
bool DartIsolate::RunFromLibrary(const std::string& library_name,
                                 const std::string& entrypoint_name,
                                 const std::vector<std::string>& args,
                                 fml::closure on_run) {
  tonic::DartState::Scope scope(this);

  auto user_entrypoint_function =
      Dart_GetField(Dart_LookupLibrary(tonic::ToDart(library_name.c_str())),
                    tonic::ToDart(entrypoint_name.c_str()));

  auto entrypoint_args = tonic::ToDart(args);

  if (!InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) {
    return false;
  }
  phase_ = Phase::Running;
  if (on_run) {
    on_run();
  }
  return true;
}

再看看 InvokeMainEntrypoint 做了什么渴杆,源碼做了精簡寥枝,主要就這兩步,我們可以在 dart 端找到對應(yīng)的函數(shù)

  • _getStartMainIsolateFunction
  • _runMainZoned
static bool InvokeMainEntrypoint(Dart_Handle user_entrypoint_function,
                                 Dart_Handle args) {

  Dart_Handle start_main_isolate_function =
      tonic::DartInvokeField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")),
                             "_getStartMainIsolateFunction", {});

  if (tonic::LogIfError(tonic::DartInvokeField(
          Dart_LookupLibrary(tonic::ToDart("dart:ui")), "_runMainZoned",
          {start_main_isolate_function, user_entrypoint_function, args}))) {
    FML_LOG(ERROR) << "Could not invoke the main entrypoint.";
    return false;
  }

  return true;
}

再往下就是 tonic 庫磁奖,這是 fuchsia 下的庫囊拜。

總結(jié)

Flutter 運(yùn)行于 iOS 之上,從源碼層面看比搭,有以下幾點(diǎn)收獲:

  • 復(fù)用了現(xiàn)有的三類 CALayer 來繪制界面冠跷,drawLayer 時(shí)會(huì)調(diào)用 takeScreenshot 來獲取 Flutter 界面的光柵圖
  • 在原生端不會(huì)建立對應(yīng)的 語義樹,需要額外生成
  • Flutter 自身會(huì)起一個(gè)完全獨(dú)立的線程環(huán)境來運(yùn)行身诺,我們需要關(guān)注的是四個(gè) TaskRunner蜜托,Platform TaskRunner 不一定是獨(dú)立的線程
  • Platform TaskRunner,原生端跟 Flutter 的所有交互都會(huì)在 Platform TaskRunner 進(jìn)行處理
  • dart 端可以通過 native 關(guān)鍵字調(diào)用 C/C++ 的函數(shù)霉赡,獲取基本類型的數(shù)據(jù)返回值橄务,性能比 channel 要好
  • FlutterViewController 將所有的手勢交互相關(guān)的都轉(zhuǎn)發(fā)給 FlutterEngine

Flutter 運(yùn)行流程

對整個(gè) Flutter 運(yùn)行的流程可以大致總結(jié)如下,主要是側(cè)重在引擎?zhèn)妊鳎琩art 那邊的流程不展開蜂挪,僅供參考:

  • 尋找 DartLibrary
  • 定位到 Entrypoint
  • 創(chuàng)建 FlutterEngine,傳入 DartLibrary 和 Entrypoint
  • 創(chuàng)建 FlutterViewController嗓化,FlutterView
  • 設(shè)置 FlutterEngineviewController
  • 創(chuàng)建 shell棠涮,啟動(dòng) Dart VM
  • 加載 DartLibrary,運(yùn)行 dart 的 Entrypoint
  • 截取 Dart UI 的界面并光柵化并 繪制 CALayer

作者其它文章

Flutter 在哈啰出行 B 端創(chuàng)新業(yè)務(wù)的實(shí)踐

如何無縫的將Flutter引入現(xiàn)有應(yīng)用刺覆?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末严肪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谦屑,更是在濱河造成了極大的恐慌驳糯,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伦仍,死亡現(xiàn)場離奇詭異结窘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)充蓝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谓苟,你說我怎么就攤上這事官脓。” “怎么了涝焙?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵卑笨,是天一觀的道長。 經(jīng)常有香客問我仑撞,道長赤兴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任隧哮,我火速辦了婚禮桶良,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沮翔。我一直安慰自己陨帆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布采蚀。 她就那樣靜靜地躺著疲牵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪榆鼠。 梳的紋絲不亂的頭發(fā)上纲爸,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音妆够,去河邊找鬼识啦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛责静,可吹牛的內(nèi)容都是我干的袁滥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼灾螃,長吁一口氣:“原來是場噩夢啊……” “哼题翻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腰鬼,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嵌赠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后熄赡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姜挺,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年彼硫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炊豪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凌箕。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖词渤,靈堂內(nèi)的尸體忽然破棺而出牵舱,到底是詐尸還是另有隱情,我是刑警寧澤缺虐,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布芜壁,位于F島的核電站,受9級特大地震影響高氮,放射性物質(zhì)發(fā)生泄漏慧妄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一剪芍、第九天 我趴在偏房一處隱蔽的房頂上張望塞淹。 院中可真熱鬧,春花似錦紊浩、人聲如沸窖铡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽费彼。三九已至,卻和暖如春口芍,著一層夾襖步出監(jiān)牢的瞬間箍铲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工鬓椭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颠猴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓小染,卻偏偏與公主長得像翘瓮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子裤翩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355