Flutter啓動流程源碼分析

前言

相信你們在學習Flutter的開始階段都看過Flutter的架構圖,以下java

咱們知道Flutter的應用層代碼由Dart編寫,Framework層提供了一系列Widget和其它API,那麼這些Dart編寫的代碼是如何在特定平臺上執行的呢,這就要從Flutter的啓動過程提及了,瞭解了Flutter的啓動過程,這個問題便迎刃而解。android

咱們經過架構圖能夠看出Embedder是由特定的平臺實現,它其實就是將Flutter移植到各平臺的中間層代碼。Embedder層是Flutter啓動的關鍵,其在應用啓動後,由平臺原生模塊經過調用該層的API執行一系列操做,好比渲染層體系的設置、相關線程的建立等,最主要的是經過Embedder層初始化Flutter Engine,Engine中會建立DartVM、各類服務協議的初始化、Platform Channels的初始化等等,然後就會在DartVM中執行dart編寫的入口方法main方法。至此,Flutter模塊就啓動成功了。ios

flutter啓動分析

安卓平臺代碼分析

安卓平臺對應的Embedder層代碼在engine源碼的 /flutter/shell/platform/android/ 目錄下。c++

咱們來根據flutter create my_app命令建立的Flutter項目demo來分析。objective-c

flutter文件資源準備和庫加載

首先,my_app應用啓動會先執行FlutterApplication,咱們來看下該類中的生命週期方法onCreate方法的實現shell

  • /flutter/shell/platform/android/io/flutter/app/FlutterApplication.java
@Override
@CallSuper
public void onCreate() {
    super.onCreate();
    FlutterMain.startInitialization(this);
}
複製代碼

FlutterMain類便是Embedder層的代碼,該類的startInitialization方法實現以下編程

  • /flutter/shell/platform/android/io/flutter/view/FlutterMain.java
public static void startInitialization(Context applicationContext) {
    startInitialization(applicationContext, new Settings());
}

public static void startInitialization(Context applicationContext, Settings settings) {
    if (Looper.myLooper() != Looper.getMainLooper()) {
      throw new IllegalStateException("startInitialization must be called on the main thread");
    }
    // Do not run startInitialization more than once.
    if (sSettings != null) {
      return;
    }

    sSettings = settings;

    long initStartTimestampMillis = SystemClock.uptimeMillis();
    initConfig(applicationContext);
    initAot(applicationContext);
    initResources(applicationContext);
    System.loadLibrary("flutter");

    long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
    nativeRecordStartTimestamp(initTimeMillis);
}
複製代碼

由以上源碼可知startInitialization方法須要在主線程中執行,該方法主要是初始化配置信息、初始化AOT模式下的變量(Debug下是JIT模式)、資源文件的初始化(主要是將asset目錄下的flutter相關資源文件copy到私有目錄下)等,最後會將以上初始化所用時間經過JNI方法傳遞到c++層作記錄。api

  • /flutter/shell/platform/android/flutter_main.cc
static void RecordStartTimestamp(JNIEnv* env,
                                 jclass jcaller,
                                 jlong initTimeMillis) {
  int64_t initTimeMicros =
      static_cast<int64_t>(initTimeMillis) * static_cast<int64_t>(1000);
  blink::engine_main_enter_ts = Dart_TimelineGetMicros() - initTimeMicros;
}
複製代碼

關鍵java類的UML類圖數組

flutter運行時環境初始化

FlutterApplication執行完onCreate方法後會執行啓動頁面MainActivity的生命週期方法。咱們發現MainActivity的onCreate方法中並無經過setContentView來設置顯示的視圖,因爲MainActivity繼承了FlutterActivity並重載了父類的onCreate方法,因此咱們看下FlutterActivity的onCreate方法緩存

  • /flutter/shell/platform/android/io/flutter/app/FlutterActivity.java
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
private final FlutterActivityEvents eventDelegate = delegate;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    eventDelegate.onCreate(savedInstanceState);
}
複製代碼

FlutterActivity繼承自Activity,那麼視圖的設置咱們推測應該是在FlutterActivityDelegate的onCreate方法中,咱們看下它的實現

  • /flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
@Override
public void onCreate(Bundle savedInstanceState) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = activity.getWindow();
        window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(0x40000000);
        window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
    }

    String[] args = getArgsFromIntent(activity.getIntent());
    FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);

    flutterView = viewFactory.createFlutterView(activity);
    if (flutterView == null) {
        FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
        flutterView = new FlutterView(activity, null, nativeView);
        flutterView.setLayoutParams(matchParent);
        activity.setContentView(flutterView);
        launchView = createLaunchView();
        if (launchView != null) {
            addLaunchView();
        }
    }

    if (loadIntent(activity.getIntent())) {
        return;
    }

    String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
    if (appBundlePath != null) {
        runBundle(appBundlePath);
    }
}
複製代碼

咱們發現代碼中有一句activity.setContentView(flutterView);即爲當前MainActivity設置顯示視圖,那麼flutterView是如何建立的呢,咱們分析該語句以前的代碼。

  1. 首先根據當前系統版原本設置沉浸式狀態欄;
  2. 獲取打開Activity時經過intent傳入的參數信息;
  3. 執行FlutterMain的ensureInitializationComplete方法;
  4. 建立FlutterNativeView;
  5. 根據FlutterNativeView建立FlutterView;
  6. 將FlutterView設置爲activity的內容視圖;
  7. 經過FlutterMain查找appBundle所在路徑,並執行appBundle;

咱們先來分析第3步驟執行FlutterMain的ensureInitializationComplete方法,先看一下具體實現

  • /flutter/shell/platform/android/io/flutter/view/FlutterMain.java
public static void ensureInitializationComplete(Context applicationContext, String[] args) {
    if (Looper.myLooper() != Looper.getMainLooper()) {
      throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
    }
    ...
    if (sInitialized) {
        return;
    }
    try {
        sResourceExtractor.waitForCompletion();

        List<String> shellArgs = new ArrayList<>();
        shellArgs.add("--icu-data-file-path=" + sIcuDataPath);
        ...
        
        String appBundlePath = findAppBundlePath(applicationContext);
        String appStoragePath = PathUtils.getFilesDir(applicationContext);
        String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
        nativeInit(applicationContext, shellArgs.toArray(new String[0]),
            appBundlePath, appStoragePath, engineCachesPath);

        sInitialized = true;
    } catch (Exception e) {
        Log.e(TAG, "Flutter initialization failed.", e);
        throw new RuntimeException(e);
    }
}
複製代碼

咱們發現該方法也要求必須在主線程中執行,且只執行一次,一旦執行過會經過sInitialized變量來進行標識下次再也不執行。try-catch代碼塊中第一句sResourceExtractor.waitForCompletion()表示要等待初始化時的資源初始化完畢後纔會向下執行,不然會一直阻塞。下面會初始化一些參數配置信息、flutter打包出的appBundle路徑、應用存儲目錄、引擎緩存目錄等信息,而後會調用JNI方法在c++層初始化這些信息,JNI方法對應的c++方法以下

  • /flutter/shell/platform/android/flutter_main.cc
void FlutterMain::Init(JNIEnv* env,
                       jclass clazz,
                       jobject context,
                       jobjectArray jargs,
                       jstring bundlePath,
                       jstring appStoragePath,
                       jstring engineCachesPath) {
  std::vector<std::string> args;
  args.push_back("flutter");
  for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
    args.push_back(std::move(arg));
  }
  auto command_line = fml::CommandLineFromIterators(args.begin(), args.end());
  auto settings = SettingsFromCommandLine(command_line);
  settings.assets_path = fml::jni::JavaStringToString(env, bundlePath);

  ...

  g_flutter_main.reset(new FlutterMain(std::move(settings)));
}
複製代碼

c++層會將傳入的參數保存到settings對象中,而後根據settings對象建立FlutterMain對象並保存到全局靜態變量g_flutter_main中,供後續flutter引擎初始化使用。

接着,會經過viewFactory建立FlutterView對象,viewFactory就是實現了ViewFactory接口的FlutterActivity對象,查看其createFlutterView方法的實現發現返回null,此時就會執行if中的代碼塊,一樣的,會經過viewFactory建立FlutterNativeView對象,咱們查看源碼發現一樣返回null,這兩個方法在FlutterActivity中的實現以下

  • /flutter/shell/platform/android/io/flutter/app/FlutterActivity.java
@Override
public FlutterView createFlutterView(Context context) {
    return null;
}

@Override
public FlutterNativeView createFlutterNativeView() {
    return null;
}
複製代碼

緊接着,會開始使用FlutterView的帶參數的構造方法建立FlutterView對象,其具體實現以下

  • /flutter/shell/platform/android/io/flutter/view/FlutterView.java
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
    super(context, attrs);

    Activity activity = (Activity) getContext();
    if (nativeView == null) {
        mNativeView = new FlutterNativeView(activity.getApplicationContext());
    } else {
        mNativeView = nativeView;
    }
    ...

    mNativeView.attachViewAndActivity(this, activity);

    mSurfaceCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            assertAttached();
            mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
        }

        ...
    };
    getHolder().addCallback(mSurfaceCallback);
	 ...

    // Configure the platform plugins and flutter channels.
    mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE);
    ...
}
複製代碼

這裏參數nativeView上文已經得出結論爲null,要先經過FlutterNativeView的構造方法建立mNativeView對象,而後經過mNativeView調用attachViewAndActivity方法將FlutterView和當前的Activity作鏈接。

接着建立當前FlutterView(要知道它繼承自SurfaceView)的mSurfaceCallback對象並添加到當前SurfaceHolder中以監聽Surface的變化(如Surface的建立、改變和銷燬等),這些變化會執行對應的回調方法,而後經過FlutterJNI的相關方法傳遞數據給Flutter engine層。

該方法中還會建立各類必要的平臺插件和platform channel(用於flutter和原生之間的各類數據傳遞)。

接下來咱們看下FlutterNativeView的構造方法實現

  • /flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java
public FlutterNativeView(Context context) {
    this(context, false);
}

public FlutterNativeView(Context context, boolean isBackgroundView) {
    mContext = context;
    mPluginRegistry = new FlutterPluginRegistry(this, context);
    mFlutterJNI = new FlutterJNI();
    mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
    mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl());
    mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
    attach(this, isBackgroundView);
    assertAttached();
    mMessageHandlers = new HashMap<>();
}
複製代碼

方法中會初始化mFlutterJNI對象,該對象的做用是經過JNI方法來傳遞信息給c++層,以便其根據不一樣的指令來通知flutter engine執行對應的操做。包括建立並啓動Flutter engine、當前FlutterView的Surface生命週期的通知、傳遞platform數據給dart層、回傳dart層調用platform層方法返回的結果數據等等。

該構造方法中有一個關鍵方法調用就是attach方法,其實現以下

  • /flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java
private void attach(FlutterNativeView view, boolean isBackgroundView) {
    mFlutterJNI.attachToNative(isBackgroundView);
}
複製代碼

經過mFlutterJNI的attachToNative方法實現java層和c++層的鏈接,實現以下

  • /flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
@UiThread
public void attachToNative(boolean isBackgroundView) {
	ensureNotAttachedToNative();
	nativePlatformViewId = nativeAttach(this, isBackgroundView);
}

private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView);
複製代碼

attachToNative方法中經過調用JNI方法nativeAttach將當前flutterJNI對象傳遞給c++層(後續一些dart層調用java層的方法就是經過該flutterJNI對象調用對應的方法實現的,好比上一篇分享的platform channel就使用到了),獲得c++層返回的nativePlatformViewId(該值很是重要,是c++層AndroidShellHolder的對象指針值,後續會經過該值調用一系列c++層的方法執行操做)並將其保存以供後續使用。

這個階段關鍵java類的UML類圖

接着看下nativeAttach方法在c++中的實現,該方法很是重要,一系列Flutter engine的初始化就是在這裏作的。

  • /flutter/shell/platform/android/platform_view_android_jni.cc
static jlong AttachJNI(JNIEnv* env,
                       jclass clazz,
                       jobject flutterJNI,
                       jboolean is_background_view) {
  fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI);
  auto shell_holder = std::make_unique<AndroidShellHolder>(
      FlutterMain::Get().GetSettings(), java_object, is_background_view);
  if (shell_holder->IsValid()) {
    return reinterpret_cast<jlong>(shell_holder.release());
  } else {
    return 0;
  }
}
複製代碼

咱們發現該方法中經過以前初始化的保存在g_flutter_main對象中的settings值和傳入的java對象flutterJNI建立std::unique_ptr對象(該對象經過指針佔有並管理AndroidShellHolder對象),該對象有效的狀況下會調用release方法返回其管理對象的指針並釋放對象的全部權,reinterpret_cast()方法將該AndroidShellHolder對象指針強制轉化爲long類型的值並返回java層保存。

接下來咱們經過AndroidShellHolder構造函數的實現來分析建立對象時都作了哪些操做

  • /flutter/shell/platform/android/android_shell_holder.cc
AndroidShellHolder::AndroidShellHolder(
    blink::Settings settings,
    fml::jni::JavaObjectWeakGlobalRef java_object,
    bool is_background_view)
    : settings_(std::move(settings)), java_object_(java_object) {
  static size_t shell_count = 1;
  auto thread_label = std::to_string(shell_count++);
  ...
  if (is_background_view) {
    thread_host_ = {thread_label, ThreadHost::Type::UI};
  } else {
    thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
                                      ThreadHost::Type::IO};
  }
  ...

  fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
  Shell::CreateCallback<PlatformView> on_create_platform_view =
      [is_background_view, java_object, &weak_platform_view](Shell& shell) {
        ...
        return platform_view_android;
      };
  Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
    return std::make_unique<Rasterizer>(shell.GetTaskRunners());
  };

  // The current thread will be used as the platform thread. Ensure that the
  // message loop is initialized.
  fml::MessageLoop::EnsureInitializedForCurrentThread();
  fml::RefPtr<fml::TaskRunner> gpu_runner;
  fml::RefPtr<fml::TaskRunner> ui_runner;
  fml::RefPtr<fml::TaskRunner> io_runner;
  fml::RefPtr<fml::TaskRunner> platform_runner =
      fml::MessageLoop::GetCurrent().GetTaskRunner();
  if (is_background_view) {
    ...
  } else {
    gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
    ui_runner = thread_host_.ui_thread->GetTaskRunner();
    io_runner = thread_host_.io_thread->GetTaskRunner();
  }
  blink::TaskRunners task_runners(thread_label,     // label
                                  platform_runner,  // platform
                                  gpu_runner,       // gpu
                                  ui_runner,        // ui
                                  io_runner         // io
  );

  shell_ =
      Shell::Create(task_runners,             // task runners
                    settings_,                // settings
                    on_create_platform_view,  // platform view create callback
                    on_create_rasterizer      // rasterizer create callback
      );

  platform_view_ = weak_platform_view;
  FML_DCHECK(platform_view_);
  ...
 }
}
複製代碼

傳入的參數is_background_view值爲false,經過前半部分代碼咱們發現會新建三個線程保存到thread_host_中,分別爲gpu_threadui_threadio_thread,而當前的線程也便是platform層的UI主線程做爲platform_thread存在,四個線程分別持有一個TaskRunner對象,對應gpu_runnerui_runnerio_runnerplatform_runner,後續會經過這些TaskRunner來將一些操做放到對應的線程中去執行,下面大體列一下各線程在Flutter engine中的主要職責。

Platform Thread GPU Thread UI Thread IO Thread
Flutter Engine的接口調用 執行設備GPU的指令 執行Dart root isolate代碼 讀取並處理圖片數據

接下來會根據四個線程對應的TaskRunner建立task_runners對象,而後經過Shell::Create()方法建立shell_對象,看下該方法的具體實現

  • /flutter/shell/common/shell.cc
std::unique_ptr<Shell> Shell::Create(
    blink::TaskRunners task_runners,
    blink::Settings settings,
    Shell::CreateCallback<PlatformView> on_create_platform_view,
    Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
  PerformInitializationTasks(settings);

  auto vm = blink::DartVM::ForProcess(settings);
  FML_CHECK(vm) << "Must be able to initialize the VM.";
  return Shell::Create(std::move(task_runners),             //
                       std::move(settings),                 //
                       vm->GetIsolateSnapshot(),            //
                       blink::DartSnapshot::Empty(),        //
                       std::move(on_create_platform_view),  //
                       std::move(on_create_rasterizer)      //
  );
}
複製代碼

首先執行初始化任務(包括初始化綁定到skia的跟蹤事件、skia引擎的初始化、國際化組件初始化等),接着根據settings值建立DartVM對象初始化Dart虛擬機,最後建立std::unique_ptr對象並返回。咱們先看一下DartVM的建立過程

  • /flutter/runtime/dart_vm.cc
fml::RefPtr<DartVM> DartVM::ForProcess(Settings settings) {
  return ForProcess(settings, nullptr, nullptr, nullptr);
}

static std::once_flag gVMInitialization;
static std::mutex gVMMutex;
static fml::RefPtr<DartVM> gVM;

fml::RefPtr<DartVM> DartVM::ForProcess(
    Settings settings,
    fml::RefPtr<DartSnapshot> vm_snapshot,
    fml::RefPtr<DartSnapshot> isolate_snapshot,
    fml::RefPtr<DartSnapshot> shared_snapshot) {
  std::lock_guard<std::mutex> lock(gVMMutex);
  std::call_once(gVMInitialization, [settings,          //
                                     vm_snapshot,       //
                                     isolate_snapshot,  //
                                     shared_snapshot    //
  ]() mutable {
    if (!vm_snapshot) {
      vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings);
    }
    ...
    if (!isolate_snapshot) {
      isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings);
    }
    ...
    gVM = fml::MakeRefCounted<DartVM>(settings,                     //
                                      std::move(vm_snapshot),       //
                                      std::move(isolate_snapshot),  //
                                      std::move(shared_snapshot)    //
    );
  });
  return gVM;
}
複製代碼

此塊代碼表示建立DartVM對象的代碼塊只執行一次,即便從多個線程中被調用也是執行一次,保證DartVM只初始化一次,下面看下DartVM的構造方法實現

  • /flutter/runtime/dart_vm.cc
DartVM::DartVM(const Settings& settings,
               fml::RefPtr<DartSnapshot> vm_snapshot,
               fml::RefPtr<DartSnapshot> isolate_snapshot,
               fml::RefPtr<DartSnapshot> shared_snapshot)
    : settings_(settings),
      vm_snapshot_(std::move(vm_snapshot)),
      isolate_snapshot_(std::move(isolate_snapshot)),
      shared_snapshot_(std::move(shared_snapshot)),
      weak_factory_(this) {
  ...

  {
    TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo");
    dart::bin::BootstrapDartIo();
    ...
  }

  ...
  DartUI::InitForGlobal();
  
  Dart_SetFileModifiedCallback(&DartFileModifiedCallback);
  {
    TRACE_EVENT0("flutter", "Dart_Initialize");
    Dart_InitializeParams params = {};
    ...
    params.create = reinterpret_cast<decltype(params.create)>(
        DartIsolate::DartIsolateCreateCallback);
    ...
    char* init_error = Dart_Initialize(&params);
    ...
  }
  ...
}
複製代碼

第一步,經過執行dart::bin::BootstrapDartIo()方法引導啓動"dart:io"事件處理程序,具體方法調用以下

  • /third_party/dart/runtime/bin/dart_io_api_impl.cc
void BootstrapDartIo() {
  // Bootstrap 'dart:io' event handler.
  TimerUtils::InitOnce();
  EventHandler::Start();
}
複製代碼

第二步,經過執行DartUI::InitForGlobal()方法註冊dart的各類本地方法,這些方法的註冊相似於java的JNI方法註冊,主要用於dart層調用c++方法(上一篇文章中經過platform channel執行dart調用platform方法就使用到了Window相關的本地方法調用),相關源碼以下

  • /flutter/lib/ui/dart_ui.cc
void DartUI::InitForGlobal() {
  if (!g_natives) {
    g_natives = new tonic::DartLibraryNatives();
    Canvas::RegisterNatives(g_natives);
    ...
    FrameInfo::RegisterNatives(g_natives);
    ...
    Window::RegisterNatives(g_natives);

    // Secondary isolates do not provide UI-related APIs.
    g_natives_secondary = new tonic::DartLibraryNatives();
    DartRuntimeHooks::RegisterNatives(g_natives_secondary);
    IsolateNameServerNatives::RegisterNatives(g_natives_secondary);
  }
}
複製代碼

這裏僅看一下Window相關的本地方法註冊

  • /flutter/lib/ui/window/window.cc
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},
  });
}
複製代碼

第三步,經過執行Dart_Initialize(&params)方法來初始化Dart運行時環境,具體實現以下,緊貼出部分代碼,其它代碼請自行查看源碼。

  • /third_party/dart/runtime/vm/dart_api_impl.cc
DART_EXPORT char* Dart_Initialize(Dart_InitializeParams* params) {
  ...
  return Dart::Init(params->vm_snapshot_data, params->vm_snapshot_instructions,
                    params->create, params->shutdown, params->cleanup,
                    params->thread_exit, params->file_open, params->file_read,
                    params->file_write, params->file_close,
                    params->entropy_source, params->get_service_assets,
                    params->start_kernel_isolate);
}
複製代碼

具體的初始化工做會由Dart::Init()方法實現

  • /third_party/dart/runtime/vm/dart.cc
char* Dart::Init(const uint8_t* vm_isolate_snapshot,
                 const uint8_t* instructions_snapshot,
                 Dart_IsolateCreateCallback create,
                 Dart_IsolateShutdownCallback shutdown,
                 Dart_IsolateCleanupCallback cleanup,
                 Dart_ThreadExitCallback thread_exit,
                 Dart_FileOpenCallback file_open,
                 Dart_FileReadCallback file_read,
                 Dart_FileWriteCallback file_write,
                 Dart_FileCloseCallback file_close,
                 Dart_EntropySource entropy_source,
                 Dart_GetVMServiceAssetsArchive get_service_assets,
                 bool start_kernel_isolate) {
  ...

  FrameLayout::Init();

  ...
  OS::Init();
  ...
  OSThread::Init();
  ...
  Isolate::InitVM();
  ...
  Api::Init();
  ...
#if defined(USING_SIMULATOR)
  Simulator::Init();
#endif
  ...
  thread_pool_ = new ThreadPool();
  {
    ...
    vm_isolate_ = Isolate::InitIsolate("vm-isolate", api_flags, is_vm_isolate);
    ...
    Object::Init(vm_isolate_);
    ...
  }
  Api::InitHandles();

  Thread::ExitIsolate();  // Unregister the VM isolate from this thread.
  ...

#ifndef DART_PRECOMPILED_RUNTIME
  if (start_kernel_isolate) {
    KernelIsolate::Run();
  }
#endif // DART_PRECOMPILED_RUNTIME

  return NULL;
}
複製代碼

至此,DartVM的初始化就完成了,最後DartVM對象會返回給Shell,Shell經過以下方法建立Shell對象

  • /flutter/shell/common/shell.cc
std::unique_ptr<Shell> Shell::Create(
    blink::TaskRunners task_runners,
    blink::Settings settings,
    fml::RefPtr<blink::DartSnapshot> isolate_snapshot,
    fml::RefPtr<blink::DartSnapshot> shared_snapshot,
    Shell::CreateCallback<PlatformView> on_create_platform_view,
    Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
  ...
  fml::AutoResetWaitableEvent latch;
  std::unique_ptr<Shell> shell;
  fml::TaskRunner::RunNowOrPostTask(
      task_runners.GetPlatformTaskRunner(),
      [&latch,                                          //
       &shell,                                          //
       task_runners = std::move(task_runners),          //
       settings,                                        //
       isolate_snapshot = std::move(isolate_snapshot),  //
       shared_snapshot = std::move(shared_snapshot),    //
       on_create_platform_view,                         //
       on_create_rasterizer                             //
  ]() {
        shell = CreateShellOnPlatformThread(std::move(task_runners),      //
                                            settings,                     //
                                            std::move(isolate_snapshot),  //
                                            std::move(shared_snapshot),   //
                                            on_create_platform_view,      //
                                            on_create_rasterizer          //
        );
        latch.Signal();
      });
  latch.Wait();
  return shell;
}
複製代碼

最終會經過Shell中的CreateShellOnPlatformThread方法在Platform Thread中建立Shell對象,因爲該方法中代碼比較多,咱們分塊來進行分析,首先看一下Shell對象的建立

  • /flutter/shell/common/shell.cc
auto shell = std::unique_ptr<Shell>(new Shell(task_runners, settings));
複製代碼

經過構造方法建立shell對象,緊接着開始執行四個關鍵的代碼塊

  • /flutter/shell/common/shell.cc
// Create the platform view on the platform thread (this thread).
auto platform_view = on_create_platform_view(*shell.get());
if (!platform_view || !platform_view->GetWeakPtr()) {
return nullptr;
}
複製代碼
  • /flutter/shell/platform/android/android_shell_holder.cc
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
Shell::CreateCallback<PlatformView> on_create_platform_view =
  [is_background_view, java_object, &weak_platform_view](Shell& shell) {
    std::unique_ptr<PlatformViewAndroid> platform_view_android;
    if (is_background_view) {
      ...

    } else {
      platform_view_android = std::make_unique<PlatformViewAndroid>(
          shell,                   // delegate
          shell.GetTaskRunners(),  // task runners
          java_object,             // java object handle for JNI interop
          shell.GetSettings()
              .enable_software_rendering  // use software rendering
      );
    }
    weak_platform_view = platform_view_android->GetWeakPtr();
    return platform_view_android;
  };
複製代碼

關鍵代碼塊一:在platform thread中根據傳入的on_create_platform_view函數建立PlatformViewAndroid對象並交由platform_view管理,如上該函數在AndroidShellHolder的構造函數中聲明。

  • /flutter/shell/common/shell.cc
// Create the IO manager on the IO thread. 
fml::AutoResetWaitableEvent io_latch;
std::unique_ptr<IOManager> io_manager;
auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner();
fml::TaskRunner::RunNowOrPostTask(
  io_task_runner,
  [&io_latch,       //
   &io_manager,     //
   &platform_view,  //
   io_task_runner   //
]() {
    io_manager = std::make_unique<IOManager>(
        platform_view->CreateResourceContext(), io_task_runner);
    io_latch.Signal();
  });
io_latch.Wait();
複製代碼

關鍵代碼塊二:在IO thread中建立IOManager對象,並交由io_manager管理。

  • /flutter/shell/common/shell.cc
// Create the rasterizer on the GPU thread.
fml::AutoResetWaitableEvent gpu_latch;
std::unique_ptr<Rasterizer> rasterizer;
fml::WeakPtr<blink::SnapshotDelegate> snapshot_delegate;
fml::TaskRunner::RunNowOrPostTask(
  task_runners.GetGPUTaskRunner(), [&gpu_latch,            //
                                    &rasterizer,           //
                                    on_create_rasterizer,  //
                                    shell = shell.get(),   //
                                    &snapshot_delegate     //
]() {
    if (auto new_rasterizer = on_create_rasterizer(*shell)) {
      rasterizer = std::move(new_rasterizer);
      snapshot_delegate = rasterizer->GetSnapshotDelegate();
    }
    gpu_latch.Signal();
  });

gpu_latch.Wait();
複製代碼
  • /flutter/shell/platform/android/android_shell_holder.cc
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell.GetTaskRunners());
};
複製代碼

關鍵代碼塊三:在GPU thread中根據傳入的on_create_rasterizer函數建立Rasterizer對象並交由rasterizer管理,如上該函數也在AndroidShellHolder的構造函數中聲明。

  • /flutter/shell/common/shell.cc
// Create the engine on the UI thread.
fml::AutoResetWaitableEvent ui_latch;
std::unique_ptr<Engine> engine;
fml::TaskRunner::RunNowOrPostTask(
  shell->GetTaskRunners().GetUITaskRunner(),
  fml::MakeCopyable([&ui_latch,                                         //
                     &engine,                                           //
                     shell = shell.get(),                               //
                     isolate_snapshot = std::move(isolate_snapshot),    //
                     shared_snapshot = std::move(shared_snapshot),      //
                     vsync_waiter = std::move(vsync_waiter),            //
                     snapshot_delegate = std::move(snapshot_delegate),  //
                     io_manager = io_manager->GetWeakPtr()              //
]() mutable {
    const auto& task_runners = shell->GetTaskRunners();

    // The animator is owned by the UI thread but it gets its vsync pulses
    // from the platform.
    auto animator = std::make_unique<Animator>(*shell, task_runners,
                                               std::move(vsync_waiter));

    engine = std::make_unique<Engine>(*shell,                        //
                                      shell->GetDartVM(),            //
                                      std::move(isolate_snapshot),   //
                                      std::move(shared_snapshot),    //
                                      task_runners,                  //
                                      shell->GetSettings(),          //
                                      std::move(animator),           //
                                      std::move(snapshot_delegate),  //
                                      std::move(io_manager)          //
    );
    ui_latch.Signal();
  }));

ui_latch.Wait();
複製代碼

關鍵代碼塊四:在UI thread中建立Engine對象,並交由engine管理。

最後會經過shell的Setup方法調用將platform_viewio_managerrasterizerengine四個unique_ptr保存到Shell對象中交由Shell對象管理

  • /flutter/shell/common/shell.cc
if (!shell->Setup(std::move(platform_view),  //
                std::move(engine),         //
                std::move(rasterizer),     //
                std::move(io_manager))     //
) {
	return nullptr;
}
複製代碼

Shell對象經過Shell::Create()建立完成後返回給AndroidShellHolder持有。至此,Embedder層就經過Shell對象與engine層創建了鏈接,後續的一切操做均可以經過Shell對象來進行。而建立好的AndroidShellHolder對象指針值又返回給了java層,最終java層即可以使用該指針值經過JNI方法調用拿到Embedder層的AndroidShellHolder對象,進而經過Shell對象向engine層發送一系列操做指令。

這個階段關鍵c++類的UML類圖

flutter中dart層代碼執行

以上流程已經爲dart層代碼執行建立好了運行時環境,那麼接下來就應該加載dart層相關的代碼執行了,這樣咱們就能夠看到dart編寫的widget顯示在MainActivity界面上了。

咱們回到上面分析的FlutterActivityDelegate的onCreate()方法中,當FlutterView和FlutterNativeView建立成功後,會經過activity.setContentView(flutterView);將FlutterView做爲activity的內容視圖,而flutter層的UI就是被渲染到FlutterView上的,因此當前MainActivity展現出來的就是咱們的flutter UI界面。

固然,代碼走到此時,dart層代碼尚未運行,因此界面上仍是顯示空白,咱們看onCreate()代碼塊的最後部分,找到appBundle而後經過runBundle方法開始執行,runBundle方法以下

  • /flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
private void runBundle(String appBundlePath) {
    if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
        FlutterRunArguments args = new FlutterRunArguments();
        ArrayList<String> bundlePaths = new ArrayList<>();
        ResourceUpdater resourceUpdater = FlutterMain.getResourceUpdater();
        if (resourceUpdater != null) {
            File patchFile = resourceUpdater.getInstalledPatch();
            JSONObject manifest = resourceUpdater.readManifest(patchFile);
            if (resourceUpdater.validateManifest(manifest)) {
                bundlePaths.add(patchFile.getPath());
            }
        }
        bundlePaths.add(appBundlePath);
        args.bundlePaths = bundlePaths.toArray(new String[0]);
        args.entrypoint = "main";
        flutterView.runFromBundle(args);
    }
}
複製代碼

第一次啓動flutter頁面isApplicationRunning()爲false,執行if語句後的代碼塊,先檢查是否有更新的flutter相關資源(用於動態更新,2019年flutter團隊的一個目標之一,這裏是先預埋了代碼,應該尚未起做用),沒有更新的bundle包,則設置對應的運行參數,而後使用flutterView.runFromBundle()方法開始執行。

  • /flutter/shell/platform/android/io/flutter/view/FlutterView.java
public void runFromBundle(FlutterRunArguments args) {
	assertAttached();
	preRun();
	mNativeView.runFromBundle(args);
	postRun();
}
複製代碼

調用FlutterNativeView的runFromBundle方法執行

  • /flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java
public void runFromBundle(FlutterRunArguments args) {
    boolean hasBundlePaths = args.bundlePaths != null && args.bundlePaths.length != 0;
    ...
    if (hasBundlePaths) {
        runFromBundleInternal(args.bundlePaths, args.entrypoint, args.libraryPath);
    } else {
        ...
    }
}

private void runFromBundleInternal(String[] bundlePaths, String entrypoint,
    String libraryPath) {
    ...
    mFlutterJNI.runBundleAndSnapshotFromLibrary(
        bundlePaths,
        entrypoint,
        libraryPath,
        mContext.getResources().getAssets()
    );

    applicationIsRunning = true;
}
複製代碼

最終是經過FlutterJNI的方法來調用JNI方法執行

  • /flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
@UiThread
public void runBundleAndSnapshotFromLibrary(
  @NonNull String[] prioritizedBundlePaths,
  @Nullable String entrypointFunctionName,
  @Nullable String pathToEntrypointFunction,
  @NonNull AssetManager assetManager
) {
	ensureAttachedToNative();
	nativeRunBundleAndSnapshotFromLibrary(
	    nativePlatformViewId,
	    prioritizedBundlePaths,
	    entrypointFunctionName,
	    pathToEntrypointFunction,
	    assetManager
	);
}

private native void nativeRunBundleAndSnapshotFromLibrary(
  long nativePlatformViewId,
  @NonNull String[] prioritizedBundlePaths,
  @Nullable String entrypointFunctionName,
  @Nullable String pathToEntrypointFunction,
  @NonNull AssetManager manager
);
複製代碼

到這裏,咱們發現會調用JNI方法傳入nativePlatformViewId參數,這個就是咱們前面提到的AndroidShellHolder對象的指針值,此時的prioritizedBundlePaths數組中只有一個值相似"/data/data/包名/flutter/flutter_assets/"的路徑值,entrypointFunctionName爲"main",pathToEntrypointFunction爲null。接下來看下JNI對應c++方法的實現

  • /flutter/shell/platform/android/platform_view_android_jni.cc
static void RunBundleAndSnapshotFromLibrary(JNIEnv* env,
                                            jobject jcaller,
                                            jlong shell_holder,
                                            jobjectArray jbundlepaths,
                                            jstring jEntrypoint,
                                            jstring jLibraryUrl,
                                            jobject jAssetManager) {
  auto asset_manager = std::make_shared<blink::AssetManager>();
  for (const auto& bundlepath :
       fml::jni::StringArrayToVector(env, jbundlepaths)) {
    ...
    const auto file_ext_index = bundlepath.rfind(".");
    if (bundlepath.substr(file_ext_index) == ".zip") {
      ...
    } else {
      asset_manager->PushBack(
          std::make_unique<blink::DirectoryAssetBundle>(fml::OpenDirectory(
              bundlepath.c_str(), false, fml::FilePermission::kRead)));

      ...
    }
  }

  auto isolate_configuration = CreateIsolateConfiguration(*asset_manager);
  ...

  RunConfiguration config(std::move(isolate_configuration),
                          std::move(asset_manager));

  {
    auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint);
    auto libraryUrl = fml::jni::JavaStringToString(env, jLibraryUrl);

    if ((entrypoint.size() > 0) && (libraryUrl.size() > 0)) {
      ...
    } else if (entrypoint.size() > 0) {
      config.SetEntrypoint(std::move(entrypoint));
    }
  }

  ANDROID_SHELL_HOLDER->Launch(std::move(config));
}
複製代碼

該段代碼首先將循環jbundlepaths中的信息將根據bundlepath建立DirectoryAssetBundle對象放到交由asset_manager管理,而後建立運行配置對象config,最後經過ANDROID_SHELL_HOLDER->Launch(std::move(config));根據運行配置信息啓動。注意ANDROID_SHELL_HOLDER是一個宏,具體實現爲

  • /flutter/shell/platform/android/platform_view_android_jni.cc
#define ANDROID_SHELL_HOLDER \
  (reinterpret_cast<shell::AndroidShellHolder*>(shell_holder))
複製代碼

便是將傳過來的java層持有的AndroidShellHolder指針值強轉爲AndroidShellHolder對象指針,此時就能夠經過對象指針調用其方法執行所須要的操做了。接下來看下Launch方法的實現

  • /flutter/shell/platform/android/android_shell_holder.cc
void AndroidShellHolder::Launch(RunConfiguration config) {
  ...
  shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
      fml::MakeCopyable([engine = shell_->GetEngine(),  //
                         config = std::move(config)     //
  ]() mutable {
        ...
        if (!engine || engine->Run(std::move(config)) ==
                           shell::Engine::RunStatus::Failure) {
          ...
        } else {
          ...
        }
      }));
}
複製代碼

咱們能夠看到engine運行dart層代碼是經過UITaskRunner在UI Thread中執行的,這就是前面說的建立UI Thread的主要做用,下面看下engine的Run方法

  • /flutter/shell/common/engine.cc
Engine::RunStatus Engine::Run(RunConfiguration configuration) {
  ...

  auto isolate_launch_status =
      PrepareAndLaunchIsolate(std::move(configuration));
      
  ...

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

shell::Engine::RunStatus Engine::PrepareAndLaunchIsolate(
    RunConfiguration configuration) {
  ...

  auto isolate_configuration = configuration.TakeIsolateConfiguration();

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

  ...

  if (configuration.GetEntrypointLibrary().empty()) {
    if (!isolate->Run(configuration.GetEntrypoint())) {
      ...
    }
  } else {
    ...
  }

  return RunStatus::Success;
}
複製代碼

最終會經過DartIsolate的Run方法來執行

  • /flutter/runtime/dart_isolate.cc
bool DartIsolate::Run(const std::string& entrypoint_name) {
  ...

  Dart_Handle entrypoint =
      Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));
  ...

  Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate"));
  if (tonic::LogIfError(isolate_lib)) {
    return false;
  }

  Dart_Handle isolate_args[] = {
      entrypoint,
      Dart_Null(),
  };

  if (tonic::LogIfError(Dart_Invoke(
          isolate_lib, tonic::ToDart("_startMainIsolate"),
          sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) {
    return false;
  }

  ...
  return true;
}
複製代碼

經過entrypoint_name建立執行入口Dart句柄entrypoint,經過Dart_LookupLibrary方法查找"dart:isolate"庫的句柄isolate_lib,這裏須要注意isolate_args[]句柄數組,第一個值爲entrypoint,第二個值爲Dart_Null(),而後經過Dart_Invoke方法調用執行

  • /third_party/dart/runtime/vm/dart_api_impl.cc
DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
                                    Dart_Handle name,
                                    int number_of_arguments,
                                    Dart_Handle* arguments) {
  ...

  String& function_name =
      String::Handle(Z, Api::UnwrapStringHandle(Z, name).raw());
  ...
  const Object& obj = Object::Handle(Z, Api::UnwrapHandle(target));
  ...
  if (obj.IsType()) {
    ...
  } else if (obj.IsNull() || obj.IsInstance()) {
    ...
  } else if (obj.IsLibrary()) {
    // Check whether class finalization is needed.
    const Library& lib = Library::Cast(obj);

    ...

    if (Library::IsPrivate(function_name)) {
      function_name = lib.PrivateName(function_name);
    }

    ...

    return Api::NewHandle(
        T, lib.Invoke(function_name, args, arg_names, respect_reflectable));
  } else {
    ...
  }
}
複製代碼

經過上面isolate_lib的建立方法Dart_LookupLibrary的實現可知obj爲一個Library對象,最後經過lib.Invoke()方法來執行dart方法,dart中對應的具體方法實現爲

  • /third_party/dart/runtime/lib/isolate_patch.dart
@pragma("vm:entry-point")
void _startMainIsolate(Function entryPoint, List<String> args) {
  _startIsolate(
      null, // no parent port
      entryPoint,
      args,
      null, // no message
      true, // isSpawnUri
      null, // no control port
      null); // no capabilities
}
複製代碼

第一個參數entryPoint即爲前文經過"main"查找的main()入口函數,也便是咱們編寫的dart中main.dart文件中的main()函數,args爲null最終經過調用如下_startIsolate方法運行。

  • /third_party/dart/runtime/lib/isolate_patch.dart
@pragma("vm:entry-point")
void _startIsolate(
    SendPort parentPort,
    Function entryPoint,
    List<String> args,
    var message,
    bool isSpawnUri,
    RawReceivePort controlPort,
    List capabilities) {
  if (controlPort != null) {
    controlPort.handler = (_) {}; // Nobody home on the control port.
  }

  ...

  RawReceivePort port = new RawReceivePort();
  port.handler = (_) {
    port.close();

    if (isSpawnUri) {
      if (entryPoint is _BinaryFunction) {
        (entryPoint as dynamic)(args, message);
      } else if (entryPoint is _UnaryFunction) {
        (entryPoint as dynamic)(args);
      } else {
        entryPoint();
      }
    } else {
      entryPoint(message);
    }
  };
  // Make sure the message handler is triggered.
  port.sendPort.send(null);
}
複製代碼

咱們在mait.dart中定義的main()函數並無任何參數,最後直接經過entryPoint()將main()函數調起。到此,咱們的dart層代碼就運行起來了。後續就是經過main()函數中的runApp()方法調用開始執行各類Widget相關綁定、Element的建立、RenderObject的建立,而後合成幀數據供下一次gsync信號接收時渲染數據到SurfaceView上。

iOS平臺代碼分析

iOS平臺對應的Embedder層代碼在engine源碼的/flutter/shell/platform/darwin/目錄下

咱們一樣根據flutter create my_app命令建立的Flutter項目demo來分析iOS平臺上flutter的啓動流程。

AppDelegate繼承自FlutterAppDelegate,咱們看下FlutterAppDelegate的生命週期執行狀況

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
- (instancetype)init {
  if (self = [super init]) {
    _lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
  }
  return self;
}

- (BOOL)application:(UIApplication*)application
    willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  return [_lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions];
}

- (BOOL)application:(UIApplication*)application
    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}
複製代碼

和安卓中相似,FluttAppDelegate生命週期方法中的處理由代理類對象_lifeCycleDelegate作具體處理,init方法中會對該對象進行初始化,咱們先看下初始化和對應的生命週期代理方法都作了什麼

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
static const char* kCallbackCacheSubDir = "Library/Caches/";

- (instancetype)init {
  if (self = [super init]) {
    std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir});
    [FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]];
    _pluginDelegates = [[NSPointerArray weakObjectsPointerArray] retain];
  }
  return self;
}

- (BOOL)application:(UIApplication*)application
    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
    if (!plugin) {
      continue;
    }
    ...
  }
  return YES;
}

- (BOOL)application:(UIApplication*)application
    willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  blink::DartCallbackCache::LoadCacheFromDisk();
  for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
    if (!plugin) {
      continue;
    }
    ...
  }
  return YES;
}
複製代碼

初始化方法中會獲取一個緩存目錄,並設置到FlutterCallbackCache中。而兩個生命週期代理方法中會遍歷_pluginDelegates中的對象,但此時數組中尚未信息。 接下來咱們看下Main.storyboard會發現應用的rootViewController爲FlutterViewController,那咱們來看一下FlutterViewController的初始化和生命週期方法

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
- (instancetype)initWithProject:(FlutterDartProject*)projectOrNil
                        nibName:(NSString*)nibNameOrNil
                         bundle:(NSBundle*)nibBundleOrNil {
  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  if (self) {
    _viewOpaque = YES;
    _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
    _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter"
                                              project:projectOrNil
                               allowHeadlessExecution:NO]);
    _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
    [_engine.get() createShell:nil libraryURI:nil];
    _engineNeedsLaunch = YES;
    [self loadDefaultSplashScreenView];
    [self performCommonViewControllerInitialization];
  }

  return self;
}
複製代碼

初始化方法中會建立FlutterEngine對象、FlutterView對象,並根據FlutterEngine對象建立Shell對象,咱們先看下FlutterEngine對象的建立

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
- (instancetype)initWithName:(NSString*)labelPrefix
                     project:(FlutterDartProject*)projectOrNil
      allowHeadlessExecution:(BOOL)allowHeadlessExecution {
  ...

  _allowHeadlessExecution = allowHeadlessExecution;
  _labelPrefix = [labelPrefix copy];

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

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

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

  [self setupChannels];

  return self;
}
複製代碼

此時傳入的projectOrNil爲nil,則會建立FlutterDartProject對象並保存到_dartProject中,FlutterDartProject初始化

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm
static blink::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
  auto command_line = shell::CommandLineFromNSProcessInfo();

  NSBundle* mainBundle = [NSBundle mainBundle];
  NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterViewController class]];

  ...

  auto settings = shell::SettingsFromCommandLine(command_line);

  settings.task_observer_add = [](intptr_t key, fml::closure callback) {
    fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback));
  };

  settings.task_observer_remove = [](intptr_t key) {
    fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
  };

  ...

  return settings;
}

- (instancetype)init {
  return [self initWithPrecompiledDartBundle:nil];
}

- (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle {
  self = [super init];

  if (self) {
    _precompiledDartBundle.reset([bundle retain]);
    _settings = DefaultSettingsForProcess(bundle);
  }

  return self;
}
複製代碼

初始化方法中會調用c++代碼方法DefaultSettingsForProcess來完成_settings對象的初始化,主要是各類flutter資源路徑設置(國際化庫、framework庫等)和一些其餘後續須要的信息配置。

而後回到FlutterEngine初始化方法中,繼續完成各類platform channel的建立。

接着看下FlutterView對象的建立

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm
- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate opaque:(BOOL)opaque {
  FML_DCHECK(delegate) << "Delegate must not be nil.";
  self = [super initWithFrame:CGRectNull];

  if (self) {
    _delegate = delegate;
    self.layer.opaque = opaque;
  }

  return self;
}
複製代碼

作了不多的事情,主要是傳入並持有實現了FlutterViewEngineDelegate協議的FlutterEngine對象。最後看下Shell的建立

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
  ...

  static size_t shellCount = 1;
  auto settings = [_dartProject.get() settings];

  if (libraryURI) {
    ...
  } else if (entrypoint) {
    ...
  } else {
    settings.advisory_script_entrypoint = std::string("main");
    settings.advisory_script_uri = std::string("main.dart");
  }

  const auto threadLabel = [NSString stringWithFormat:@"%@.%zu", _labelPrefix, shellCount++];
  
  fml::MessageLoop::EnsureInitializedForCurrentThread();

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

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

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

  if (shell::IsIosEmbeddedViewsPreviewEnabled()) {
    ...
  } else {
    blink::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 = shell::Shell::Create(std::move(task_runners),  // task runners
                                  std::move(settings),      // settings
                                  on_create_platform_view,  // platform view creation
                                  on_create_rasterizer      // rasterzier creation
    );
  }
  ...
  return _shell != nullptr;
}
複製代碼

該方法的實現和安卓中的AndroidShellHolder構造方法實現相似,主要新建立了三個線程gpu_threadui_threadio_thread,加上平臺的UI線程做爲platform_thread一共四個關鍵線程。線程的做用能夠參考安卓中的說明。而後會經過Shell::Create()方法建立engine層的Shell對象,後邊的engine初始化、DartVM初始化和其餘一系列對象建立都和安卓上面分析的同樣了,這裏再也不贅述。

回過頭來看一下FlutterViewController中初始化方法,上面一系列操做執行後,會進行各類必要的通知註冊,以便接收到通知後作出響應。

至此FlutterViewController的初始化就完成了,咱們能夠看出整個的初始化就是對flutter engine的初始化,那麼iOS平臺dart層代碼是在哪一步執行的呢,咱們下面看一下FlutterViewController的生命週期方法viewWillAppear方法

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
- (void)viewWillAppear:(BOOL)animated {
  TRACE_EVENT0("flutter", "viewWillAppear");

  if (_engineNeedsLaunch) {
    [_engine.get() launchEngine:nil libraryURI:nil];
    _engineNeedsLaunch = NO;
  }
  [_engine.get() setViewController:self];

  if (_viewportMetrics.physical_width)
    [self surfaceUpdated:YES];
  [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"];

  [super viewWillAppear:animated];
}
複製代碼

這裏_engineNeedsLaunch在FlutterViewController初始化的時候被設置爲YES,則會經過_engine開始啓動引擎

  • /flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil {
  // Launch the Dart application with the inferred run configuration.
  self.shell.GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable(
      [engine = _shell->GetEngine(),
       config = [_dartProject.get() runConfigurationForEntrypoint:entrypoint
                                                     libraryOrNil:libraryOrNil]  //
  ]() mutable {
        if (engine) {
          auto result = engine->Run(std::move(config));
          if (result == shell::Engine::RunStatus::Failure) {
            FML_LOG(ERROR) << "Could not launch engine with configuration.";
          }
        }
      }));
}
複製代碼

這裏engine運行dart層代碼是經過UITaskRunner在UI Thread中執行的,該方法和安卓Embedder層AndroidShellHolder的Launch方法功能相同。不過咱們發現該方法兩個參數都爲nil,而安卓中的entrypoint爲"main",那麼iOS中最終是如何執行Dart應用程序執行main()方法的呢,咱們看下RunConfiguration類的聲明中一些變量的聲明

  • /flutter/shell/common/run_configuration.cc
class RunConfiguration {
 public:
  ...

  RunConfiguration(RunConfiguration&&);

  ~RunConfiguration();

  ...

  const std::string& GetEntrypoint() const;

  const std::string& GetEntrypointLibrary() const;
  ...

 private:
  ...
  std::string entrypoint_ = "main";
  std::string entrypoint_library_ = "";

  FML_DISALLOW_COPY_AND_ASSIGN(RunConfiguration);
}
複製代碼

結果咱們發現RunConfiguration對象建立時默認entrypoint即爲"main",因此不用主動設置entrypoint,最終Dart代碼執行時就會將main()方法做爲入口函數執行。後續的一些engine層的操做和安卓同樣了,這裏也再也不贅述。

這個階段關鍵objective-c和c++類的UML類圖

總結

經過以上安卓和iOS平臺的源碼分析,咱們已經對flutter應用的啓動有了一個大體的瞭解,而Dart中編寫的Widget最終是如何繪製到平臺View上的呢,你們能夠閱讀以前本號分享的一篇文章《Flutter視圖繪製》,相信你們會獲得想要的答案。

說明:

文章轉載自對應的「Flutter編程指南」微信公衆號,更多Flutter相關技術文章打開微信掃描二維碼關注微信公衆號獲取。

相關文章
相關標籤/搜索