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