preface

A brief analysis of the Startup process of Flutter on Android platform

Let’s take a look at what the native layer does in between.

Since there is a lot of code, I will put the instructions in the form of comments in the code and remove unnecessary codeCopy the code

FlutterLoader

In the startInitialization () method in the flutterLoader:

public void startInitialization(@NonNull Context applicationContext, @NonNull FlutterLoader.Settings settings) { if (this.settings == null) { if (Looper.myLooper() ! = Looper.getMainLooper()) { throw new IllegalStateException("startInitialization must be called on the main thread"); } else { .... Callable<FlutterLoader.InitResult> initTask = new Callable<FlutterLoader.InitResult>() { public FlutterLoader.InitResult  call() { .... /// here is system. loadLibrary("flutter") executed on the child thread; . return new FlutterLoader.InitResult(PathUtils.getFilesDir(appContext), PathUtils.getCacheDirectory(appContext), PathUtils.getDataDirectory(appContext)); }}; this.initResultFuture = Executors.newSingleThreadExecutor().submit(initTask); }}}Copy the code

System.loadLibrary(“flutter”); Instead of simply loading the flutter framework code, it ends up in the native JNI_OnLoad method:

// This is called by the VM when the shared library is first loaded. JNIEXPORT jint JNI_OnLoad(JavaVM* vm, FML ::jni::InitJavaVM(VM); FML ::jni::InitJavaVM(VM); JNIEnv* env = fml::jni::AttachCurrentThread(); bool result = false; / / registered FlutterMain. Result = flutter: : FlutterMain: : Register (env); FML_CHECK(result); / / registered PlatformView / / here will be a lot of methods, the c + + and Java can call each other the result = flutter: : PlatformViewAndroid: : Register (env); FML_CHECK(result); VSyncWaiter. // The method in Java's VSyncWaiter class is mapped to the method in // native's VsyncWaiterAndroid. Can call each other the result = flutter: : VsyncWaiterAndroid: : Register (env); FML_CHECK(result); return JNI_VERSION_1_4; }Copy the code
Tip: PlatformViewAndroid path is: the engine/shell/platform/android have interest, you can have a lookCopy the code

Overall, the JVM is basically saved, while the c++ and Java methods are mapped so that they can call each other.

At this point, the native code drawn in FlutterApplication is simply summarized, and then the native code called in FlutterActivity.

FlutterActivity & onCreate

It’s worth mentioning that so far if you search for the FlutterActivity class, you’ll find two:

Android /FlutterActivity This is the latest app/FlutterActivity expiredCopy the code

After a series of calls, detailed in this article: Flutter startup Process on Android

FlutterEngine is initialized with the following constructor:

/ / is very long, /** Fully disables {@code FlutterEngine} constructor. */ works freely without any additional information. @NonNull Context context, @NonNull FlutterLoader flutterLoader, @NonNull FlutterJNI flutterJNI, @NonNull PlatformViewsController platformViewsController, @Nullable String[] dartVmArgs, boolean automaticallyRegisterPlugins, boolean waitForRestorationData) { this.dartExecutor = new DartExecutor(flutterJNI, context.getAssets()); this.dartExecutor.onAttachedToJNI(); accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI); keyEventChannel = new KeyEventChannel(dartExecutor); lifecycleChannel = new LifecycleChannel(dartExecutor); localizationChannel = new LocalizationChannel(dartExecutor); mouseCursorChannel = new MouseCursorChannel(dartExecutor); navigationChannel = new NavigationChannel(dartExecutor); platformChannel = new PlatformChannel(dartExecutor); restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData); settingsChannel = new SettingsChannel(dartExecutor); systemChannel = new SystemChannel(dartExecutor); textInputChannel = new TextInputChannel(dartExecutor); this.localizationPlugin = new LocalizationPlugin(context, localizationChannel); this.flutterJNI = flutterJNI; flutterLoader.startInitialization(context.getApplicationContext()); / / / attention here flutterLoader. EnsureInitializationComplete (context, dartVmArgs); flutterJNI.addEngineLifecycleListener(engineLifecycleListener); flutterJNI.setPlatformViewsController(platformViewsController); flutterJNI.setLocalizationPlugin(localizationPlugin); attachToJni(); // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if // possible. this.renderer = new FlutterRenderer(flutterJNI); this.platformViewsController = platformViewsController; this.platformViewsController.onAttachedToJNI(); this.pluginRegistry = new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader); if (automaticallyRegisterPlugins) { registerPlugins(); }}Copy the code

The entire constructor initializes a large number of channels and registers some native methods, where:

flutterLoader.ensureInitializationComplete(context, dartVmArgs);
Copy the code

Will be transferred to Native, the detailed code is as follows:

/// This method blocks, Until completion of the native system execution public void ensureInitializationComplete (@ NonNull Context applicationContext, @Nullable String[] args) { if (initialized) { return; } if (Looper.myLooper() ! = Looper.getMainLooper()) { throw new IllegalStateException( "ensureInitializationComplete must be called on the main thread"); } if (settings == null) { throw new IllegalStateException( "ensureInitializationComplete must be called after startInitialization"); } // collect various file paths try {InitResult result = initResultFuture.get(); List<String> shellArgs = new ArrayList<>(); shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat"); ApplicationInfo applicationInfo = getApplicationInfo(applicationContext); shellArgs.add( "--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + DEFAULT_LIBRARY); if (args ! = null) { Collections.addAll(shellArgs, args); } String kernelPath = null; if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) { String snapshotAssetPath = result.dataDirPath + File.separator + flutterAssetsDir; kernelPath = snapshotAssetPath + File.separator + DEFAULT_KERNEL_BLOB; shellArgs.add("--" + SNAPSHOT_ASSET_PATH_KEY + "=" + snapshotAssetPath); shellArgs.add("--" + VM_SNAPSHOT_DATA_KEY + "=" + vmSnapshotData); shellArgs.add("--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + isolateSnapshotData); } else { shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + aotSharedLibraryName); // Most devices can load the AOT shared library based on the library name // with no directory path. Provide a fully qualified path to the library // as a workaround for devices where that fails. shellArgs.add( "--" + AOT_SHARED_LIBRARY_NAME + "=" + applicationInfo.nativeLibraryDir + File.separator + aotSharedLibraryName); } shellArgs.add("--cache-dir-path=" + result.engineCachesPath); if (settings.getLogTag() ! = null) { shellArgs.add("--log-tag=" + settings.getLogTag()); } long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis; // TODO(cyanlaz): Remove this when dynamic thread merging is done. // https://github.com/flutter/flutter/issues/59930 Bundle bundle = applicationInfo.metaData; if (bundle ! = null) { boolean use_embedded_view = bundle.getBoolean("io.flutter.embedded_views_preview"); if (use_embedded_view) { shellArgs.add("--use-embedded-view"); NativeInit (applicationContext, shellArgs. ToArray (New String[0]), kernelPath, result.appStoragePath, result.engineCachesPath, initTimeMillis); initialized = true; } catch (Exception e) { Log.e(TAG, "Flutter initialization failed.", e); throw new RuntimeException(e); }}Copy the code

The relevant information is passed to flutterjni.nativeInit, that is:

Public static native void nativeInit(@nonnull Context, @nonnull String[] args, @Nullable String bundlePath, @NonNull String appStoragePath, @NonNull String engineCachesPath, long initTimeMillis);Copy the code

To the native layer, remember the flutterMain method we registered in the previous section?

FlutterMain::Register

Bool FlutterMain::Register(JNIEnv* env) {static const JNINativeMethod methods[] = {{// "nativeInit", .signature = "(Landroid/content/Context; [Ljava/lang/String;Ljava/" "lang/String;Ljava/lang/String;Ljava/lang/String; <void*>(&Init),}, { .name = "nativePrefetchDefaultFontManager", .signature = "()V", .fnPtr = reinterpret_cast<void*>(&PrefetchDefaultFontManager), }, };  jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI"); if (clazz == nullptr) { return false;  } return env->RegisterNatives(clazz, methods, fml::size(methods)) == 0; }Copy the code

A pointer.fnptr = reinterpret_cast<void*>(&Init) pulls its FlutterMain::Init method.

void FlutterMain::Init(JNIEnv* env, jclass clazz, jobject context, jobjectArray jargs, jstring kernelPath, jstring appStoragePath, jstring engineCachesPath, jlong initTimeMillis) { std::vector<std::string> args; ///tag args.push_back("flutter"); // save the path information we collected above into args /// use 'j' to represent the path passed by Java. 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); Int64_t init_time_micros = initTimeMillis * 1000; settings.engine_start_timestamp = std::chrono::microseconds(Dart_TimelineGetMicros() - init_time_micros); // Restore the callback cache. // TODO(chinmaygarde): Route all cache file access through FML and remove this // setter. flutter::DartCallbackCache::SetCachePath( fml::jni::JavaStringToString(env, appStoragePath)); / / / initializes the cache path FML: : paths: : InitializeAndroidCachesPath (FML: : jni: : JavaStringToString (env, engineCachesPath)); / / / load the cached flutter: : DartCallbackCache: : LoadCacheFromDisk (); if (! flutter::DartVM::IsRunningPrecompiledCode() && kernelPath) { // Check to see if the appropriate kernel files are present  and configure // settings accordingly. auto application_kernel_path = fml::jni::JavaStringToString(env, kernelPath); if (fml::IsFile(application_kernel_path)) { settings.application_kernel_asset = application_kernel_path; } } 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); }; #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 // Not thread safe. Will be removed when FlutterMain is refactored to no // longer be a singleton. g_flutter_main.reset(new FlutterMain(std::move(settings))); g_flutter_main->SetupObservatoryUriCallback(env); }Copy the code

Above is mainly for Java came to save the data, thus by flutterLoader. EnsureInitializationComplete caused by complete, native behind it executes attachToJni ().

FlutterActivity& attachToJni()

AttachToJni () will call flutterJNI. AttachToNative (false) :

// When this is done, May communicate with engine with android @ UiThread public void attachToNative (Boolean isBackgroundView) {ensureRunningOnMainThread (); ensureNotAttachedToNative(); nativePlatformViewId = nativeAttach(this, isBackgroundView); } private native long nativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView);Copy the code

This method calls native:

static jlong AttachJNI(JNIEnv* env, jclass clazz, jobject flutterJNI, jboolean is_background_view) { fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI); std::shared_ptr<PlatformViewAndroidJNI> jni_facade = std::make_shared<PlatformViewAndroidJNIImpl>(java_object); Auto shell_holder = STD ::make_unique<AndroidShellHolder>(FlutterMain::Get().getSettings (), jni_facade, is_background_view); if (shell_holder->IsValid()) { return reinterpret_cast<jlong>(shell_holder.release()); } else { return 0; }}Copy the code

Let’s look at the implementation of AndroidShellholder.cc

AndroidShellHolder & GPU/UI/IO thread creation

It has a 100-plus line constructor:

AndroidShellHolder::AndroidShellHolder( flutter::Settings settings, std::shared_ptr<PlatformViewAndroidJNI> jni_facade, bool is_background_view) : settings_(std::move(settings)), jni_facade_(jni_facade) { static size_t shell_count = 1; auto thread_label = std::to_string(shell_count++); FML_CHECK(pthread_key_create(&thread_destruct_key_, ThreadDestructCallback) == 0); If (is_background_view) {thread_host_ = {thread_label, ThreadHost::Type::UI}; } else {/// create three threads respectively UI gpuio thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU | ThreadHost::Type::IO}; } Detach from JNI when the UI and raster threads exit. It turns our draw instruction into the GPU instruction auto jni_exit_task([key = thread_destruct_key_]() {FML_CHECK(pthread_setspecific(key, reinterpret_cast<void*>(1)) == 0); }); thread_host_.ui_thread->GetTaskRunner()->PostTask(jni_exit_task); if (! is_background_view) { thread_host_.raster_thread->GetTaskRunner()->PostTask(jni_exit_task); } fml::WeakPtr<PlatformViewAndroid> weak_platform_view; Shell::CreateCallback<PlatformView> on_create_platform_view = [is_background_view, &jni_facade, &weak_platform_view](Shell& shell) { std::unique_ptr<PlatformViewAndroid> platform_view_android; if (is_background_view) { ... PlatformViewAndroid platform_view_android = STD ::make_unique<PlatformViewAndroid> // delegate shell.GetTaskRunners(), // task runners jni_facade, // JNI interop shell.GetSettings() .enable_software_rendering // use software rendering ); } weak_platform_view = platform_view_android->GetWeakPtr(); shell.OnDisplayUpdates(DisplayUpdateType::kStartup, {Display(jni_facade->GetDisplayRefreshRate())}); return platform_view_android; }; Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) { return std::make_unique<Rasterizer>(shell); }; // The current thread will be used as The platform thread. Ensure that The message loop is initialized The message loop / / gpu/UI/IO they also have their own MSG loop 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) { auto single_task_runner = thread_host_.ui_thread->GetTaskRunner(); gpu_runner = single_task_runner; ui_runner = single_task_runner; io_runner = single_task_runner; } else { gpu_runner = thread_host_.raster_thread->GetTaskRunner(); ui_runner = thread_host_.ui_thread->GetTaskRunner(); io_runner = thread_host_.io_thread->GetTaskRunner(); } flutter::TaskRunners task_runners(thread_label, // label platform_runner, // platform gpu_runner, // raster ui_runner, // ui io_runner // io ); / / / improve the UI and the gpu thread level / / / thread value, the smaller level, the higher the task_runners. GetRasterTaskRunner () - > PostTask ([] () {/ / Android describes - 8 as "the most important display threads, for // compositing the screen and retrieving input events". Conservatively // set the raster thread to slightly lower priority than it. if (::setpriority(PRIO_PROCESS, gettid(), -5) ! = 0) { // Defensive fallback. Depending on the OEM, it may not be possible // to set priority to -5. if (::setpriority(PRIO_PROCESS, gettid(), -2) ! = 0) { FML_LOG(ERROR) << "Failed to set GPU task runner priority"; }}}); task_runners.GetUITaskRunner()->PostTask([]() { if (::setpriority(PRIO_PROCESS, gettid(), -1) ! = 0) { FML_LOG(ERROR) << "Failed to set UI task runner priority"; }}); Shell_ = shell ::Create(task_runners, // task runners GetDefaultPlatformData(), // window data 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_valid_ = shell_ ! = nullptr; }Copy the code

Shell

Let’s take a look at shell_ creation:

std::unique_ptr<Shell> Shell::Create( TaskRunners task_runners, const PlatformData platform_data, Settings settings, Shell::CreateCallback<PlatformView> on_create_platform_view, Shell::CreateCallback<Rasterizer> on_create_rasterizer) { PerformInitializationTasks(settings); PersistentCache::SetCacheSkSL(settings.cache_sksl); TRACE_EVENT0("flutter", "Shell::Create"); /// Create a VM. Auto VM = DartVMRef::Create(Settings); FML_CHECK(vm) << "Must be able to initialize the VM."; auto vm_data = vm->GetVMData(); return Shell::Create(std::move(task_runners), // std::move(platform_data), // std::move(settings), // vm_data->GetIsolateSnapshot(), // isolate snapshot on_create_platform_view, // on_create_rasterizer, // std::move(vm) // ); }Copy the code

DartVMRef::Create

DartVMRef DartVMRef::Create(Settings settings, fml::RefPtr<DartSnapshot> vm_snapshot, fml::RefPtr<DartSnapshot> isolate_snapshot) { std::scoped_lock lifecycle_lock(gVMMutex); . If (auto VM = gvm. lock()) {FML_DLOG(WARNING) << "CHS to create a VM in a process where one  was " "already running. Ignoring arguments for current VM " "create call and reusing the old VM."; // There was already a running VM in the process, return DartVMRef{std::move(vm)}; }... Delete some code // If not, create a new virtual machine auto isolate_name_server = STD ::make_shared<IsolateNameServer>(); auto vm = DartVM::Create(std::move(settings), // std::move(vm_snapshot), // std::move(isolate_snapshot), // isolate_name_server // ); . Return DartVMRef{STD ::move(vm)}; }Copy the code

We continue to see shell to create, you will call CreateShellOnPlatformThread.

Shell & CreateShellOnPlatformThread

std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread( DartVMRef vm, TaskRunners task_runners, const PlatformData platform_data, Settings settings, fml::RefPtr<const DartSnapshot> isolate_snapshot, const Shell::CreateCallback<PlatformView>& on_create_platform_view, const Shell::CreateCallback<Rasterizer>& on_create_rasterizer) { ... Auto shell = STD ::unique_ptr< shell >(new shell (STD ::move(VM), task_runners, Settings)); // Create a rasterizer that works on the GPU thread. STD :: PROMISE < STD ::unique_ptr<Rasterizer>> rasterizer_PROMISE; auto rasterizer_future = rasterizer_promise.get_future(); std::promise<fml::WeakPtr<SnapshotDelegate>> snapshot_delegate_promise; auto snapshot_delegate_future = snapshot_delegate_promise.get_future(); fml::TaskRunner::RunNowOrPostTask( task_runners.GetRasterTaskRunner(), [&rasterizer_promise, // &snapshot_delegate_promise, on_create_rasterizer, // shell = shell.get() // ]() { TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem"); std::unique_ptr<Rasterizer> rasterizer(on_create_rasterizer(*shell)); snapshot_delegate_promise.set_value(rasterizer->GetSnapshotDelegate()); rasterizer_promise.set_value(std::move(rasterizer)); }); Auto platform_view = on_create_platform_view(*shell.get()); // Create platform view. auto platform_view = on_create_platform_view(*shell. if (! platform_view || ! platform_view->GetWeakPtr()) { return nullptr; } // Ask the platform view for the vsync waiter. This will be used by the engine // to create the animator. auto vsync_waiter = platform_view->CreateVSyncWaiter(); if (! vsync_waiter) { return nullptr; }... Delete some code... / / / to the IO thread post task to create the IO manager FML: : TaskRunner: : RunNowOrPostTask (io_task_runner, [& io_manager_promise, // &weak_io_manager_promise, // &unref_queue_promise, // platform_view = platform_view->GetWeakPtr(), // io_task_runner, // is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() // ]() { TRACE_EVENT0("flutter", "ShellSetupIOSubsystem"); auto io_manager = std::make_unique<ShellIOManager>( platform_view.getUnsafe()->CreateResourceContext(), is_backgrounded_sync_switch, io_task_runner); weak_io_manager_promise.set_value(io_manager->GetWeakPtr()); unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue()); io_manager_promise.set_value(std::move(io_manager)); }); // Send dispatcher_maker to the engine constructor because shell won't have // platform_view set until Shell::Setup is called later. auto dispatcher_maker = platform_view->GetDispatcherMaker(); STD ::promise< STD ::unique_ptr< engine >> engine_PROMISE; auto engine_future = engine_promise.get_future(); fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetUITaskRunner(), fml::MakeCopyable([&engine_promise, // shell = shell.get(), // &dispatcher_maker, // &platform_data, // isolate_snapshot = std::move(isolate_snapshot), // vsync_waiter = std::move(vsync_waiter), // &weak_io_manager_future, // &snapshot_delegate_future, // &unref_queue_future // ]() mutable { TRACE_EVENT0("flutter", "ShellSetupUISubsystem"); const auto& task_runners = shell->GetTaskRunners(); Auto animator = STD ::make_unique< animator >(*shell, task_runners, STD ::move(vsync_waiter)); engine_promise.set_value(std::make_unique<Engine>( *shell, // dispatcher_maker, // *shell->GetDartVM(), // std::move(isolate_snapshot), // task_runners, // platform_data, // shell->GetSettings(), // std::move(animator), // weak_io_manager_future.get(), // unref_queue_future.get(), // snapshot_delegate_future.get() // )); })); if (! shell->Setup(std::move(platform_view), // engine_future.get(), // rasterizer_future.get(), // io_manager_future.get()) // ) { return nullptr; } return shell; }Copy the code

Because the code is too long, here’s a summary:

After shell initialization, the following objects are also created on the IO/UI/GPU /platform threads:

  • The Rasterizer works on the GPU thread and is responsible for converting draw instructions to GPU instructions

  • Platform View in current thread (platform thread)

  • Engine and Animator work on the UI thread

    Flutter will eventually apply the VSync signal to the platformView via the animator

Let’s take a look at the initialization of Engine in the code above.

Engine

Relatively little code, but a lot of things to connect:

Engine::Engine(Delegate& delegate,
               const PointerDataDispatcherMaker& dispatcher_maker,
               DartVM& vm,
               fml::RefPtr<const DartSnapshot> isolate_snapshot,
               TaskRunners task_runners,
               const PlatformData platform_data,
               Settings settings,
               std::unique_ptr<Animator> animator,
               fml::WeakPtr<IOManager> io_manager,
               fml::RefPtr<SkiaUnrefQueue> unref_queue,
               fml::WeakPtr<SnapshotDelegate> snapshot_delegate)
    : Engine(delegate,
             dispatcher_maker,
             vm.GetConcurrentWorkerTaskRunner(),
             task_runners,
             settings,
             std::move(animator),
             io_manager,
             nullptr) {
  runtime_controller_ = std::make_unique<RuntimeController>(
      *this,                                 // runtime delegate
      &vm,                                   // VM
      std::move(isolate_snapshot),           // isolate snapshot
      task_runners_,                         // task runners
      std::move(snapshot_delegate),          // snapshot delegate
      GetWeakPtr(),                          // hint freed delegate
      std::move(io_manager),                 // io manager
      std::move(unref_queue),                // Skia unref queue
      image_decoder_.GetWeakPtr(),           // image decoder
      settings_.advisory_script_uri,         // advisory script uri
      settings_.advisory_script_entrypoint,  // advisory script entrypoint
      settings_.idle_notification_callback,  // idle notification callback
      platform_data,                         // platform data
      settings_.isolate_create_callback,     // isolate create callback
      settings_.isolate_shutdown_callback,   // isolate shutdown callback
      settings_.persistent_isolate_data      // persistent isolate data
  );
}
Copy the code

Create a runtime_controller_. You can think of the Runtime Controller as a link between the native, platform and flutter.

summary

This may be a bit confusing, but the Flutter Activity will create an initialization shell when registering flutterMain. During this initialization, three threads will be created, four if the current thread is included:

  • Platform thread (current thread)
  • The UI thread
  • Gpu thread
  • IO thread

And initialize a series of important objects.

Ok, back to the main thread, onCreate() has passed, now we can look at the onStart() life cycle:

Flutter startup Process analysis on Android platform

FlutterActivity & onStart

This method calls delegate.onstart (); And finally call FlutterJNI’s native method:

  private native void nativeRunBundleAndSnapshotFromLibrary(
      long nativePlatformViewId,
      @NonNull String bundlePath,
      @Nullable String entrypointFunctionName,
      @Nullable String pathToEntrypointFunction,
      @NonNull AssetManager manager);
Copy the code

From there, the endpoint is to execute the Dart code.

Launch

Then let’s look at native methods:

(Location at platform_view_android_jni_imp.cc)

static void RunBundleAndSnapshotFromLibrary(JNIEnv* env, jobject jcaller, jlong shell_holder, jstring jBundlePath, jstring jEntrypoint, jstring jLibraryUrl, jobject jAssetManager) { ... ANDROID_SHELL_HOLDER->Launch(STD ::move(config)); android_shell_launch (STD ::move(config));  }Copy the code

Launch(STD ::move(config)); Methods:

void AndroidShellHolder::Launch(RunConfiguration config) { if (! IsValid()) { return; } shell_->RunEngine(std::move(config)); }Copy the code

The run engine method is called again:

void Shell::RunEngine( RunConfiguration run_configuration, const std::function<void(Engine::RunStatus)>& result_callback) { ... / / / delete some code to the UI thread to post a task 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); })); }Copy the code

Engin.run()

Engine::RunStatus Engine::Run(RunConfiguration configuration) { if (! configuration.IsValid()) { FML_LOG(ERROR) << "Engine run configuration was invalid."; return RunStatus::Failure; } / / / get the dart to execute code entry point / / / this is the main method from mian. Dart last_entry_point_ = configuration. GetEntrypoint (); last_entry_point_library_ = configuration.GetEntrypointLibrary(); . LaunchRootIsolate if (! runtime_controller_->LaunchRootIsolate( settings_, // configuration.GetEntrypoint(), // configuration.GetEntrypointLibrary(), // configuration.TakeIsolateConfiguration()) // ) { return RunStatus::Failure; }... Remove part of the code return Engine: : RunStatus: : Success; }Copy the code

LaunchRootIsolate

bool RuntimeController::LaunchRootIsolate( const Settings& settings, std::optional<std::string> dart_entrypoint, std::optional<std::string> dart_entrypoint_library, std::unique_ptr<IsolateConfiguration> isolate_configuration) { if (root_isolate_.lock()) { FML_LOG(ERROR) << "Root isolate was already running."; return false; } / / / to create a "run" the root of isolate auto strong_root_isolate = DartIsolate: : CreateRunningRootIsolate (Settings, Task_runners_, // STD ::make_unique<PlatformConfiguration>(this), // platform configuration snapshot_delegate_, // hint_freed_delegate_, // io_manager_, // IO management runs on I/O threads unref_queue_, // image_decoder_, // Advisory_script_entrypoint_, // DartIsolate::Flags{}, // isolate_create_callback_, // dart_entrypoint (main.dart) dart_entrypoint_library, STD ::move(isolate_configuration) //).lock(); . Return true; }Copy the code

Let’s look at DartIsolate’s CreateRunningRootIsolate method

DartIsolate::CreateRunningRootIsolate

std::weak_ptr<DartIsolate> DartIsolate::CreateRunningRootIsolate( const Settings& settings, fml::RefPtr<const DartSnapshot> isolate_snapshot, TaskRunners task_runners, std::unique_ptr<PlatformConfiguration> platform_configuration, fml::WeakPtr<SnapshotDelegate> snapshot_delegate, fml::WeakPtr<HintFreedDelegate> hint_freed_delegate, fml::WeakPtr<IOManager> io_manager, fml::RefPtr<SkiaUnrefQueue> skia_unref_queue, fml::WeakPtr<ImageDecoder> image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, Flags isolate_flags, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback, std::optional<std::string> dart_entrypoint, std::optional<std::string> dart_entrypoint_library, std::unique_ptr<IsolateConfiguration> isolate_configration) { ... Delete code /// there is an ISOLATE created but not running auto ISOLATE = CreateRootIsolate(Settings, // isolate_snapshot, // task_runners, // std::move(platform_configuration), // snapshot_delegate, // hint_freed_delegate, // io_manager, // skia_unref_queue, // image_decoder, // advisory_script_uri, // advisory_script_entrypoint, // isolate_flags, // isolate_create_callback, // isolate_shutdown_callback // ) .lock(); . Delete some code (mainly check the status of the ISOLATE) // Notice this method if (! isolate->RunFromLibrary(dart_entrypoint_library, // dart_entrypoint, // settings.dart_entrypoint_args // )) { FML_LOG(ERROR) << "Could not run the run main Dart entrypoint."; return {}; } if (settings.root_isolate_shutdown_callback) { isolate->AddIsolateShutdownCallback( settings.root_isolate_shutdown_callback); } shutdown_on_error.Release(); return isolate; }Copy the code

After creating the ISOLATE, call the RunFromLibrary method further:

Tip: Pay attention to the parameters this procedure carries.

bool DartIsolate::RunFromLibrary(std::optional<std::string> library_name, std::optional<std::string> entrypoint, const std::vector<std::string>& args) { TRACE_EVENT0("flutter", "DartIsolate::RunFromLibrary"); /// Isolate is not ready, exit if (phase_! = Phase::Ready) { return false; } tonic::DartState::Scope scope(this); . // Call InvokeMainEntrypoint if (! InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) { return false; Phase_ = Phase::Running; phase_ = Phase::Running; return true; }Copy the code

InvokeMainEntrypoint:

[[nodiscard]] static bool InvokeMainEntrypoint( Dart_Handle user_entrypoint_function, Dart_Handle args) { ... /// this entry point can also be customized, 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; }Copy the code

summary

This concludes a brief introduction of the whole flutter startup from platform to native.

I have just started to learn about Native, so as to further understand the operation principle of Flutter. If there are any mistakes, please point them out. Thank you.

series

Flutter mimics netease Cloud music App

Flutter&Android startup page (splash screen page) loading process and optimization scheme

Flutter version of imitation. Parallax effect of Zhihu list

Flutter — Realize progressive card switching for netease Cloud Music

Flutter imitates flush list of self-selected stocks