In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-03 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces "what did the Native layer do when Flutter was started on the Android platform". In the daily operation, I believe that many people have doubts about what the Native layer did when Flutter was started on the Android platform. The editor consulted all kinds of materials and sorted out a simple and useful method of operation. I hope it will be helpful to answer the question of "what did the Native layer do when Flutter is started on the Android platform?" Next, please follow the editor to study!
FlutterLoader
In the startInitialization () method in 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 initTask = new Callable () {public FlutterLoader.InitResult call () {.... / / here is the 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);}
System.loadLibrary ("flutter"); instead of simply loading the flutter framework code, it ends up in the JNI_OnLoad method in native:
/ / This is called by the VM when the shared library is first loaded. JNIEXPORT jint JNI_OnLoad (JavaVM* vm, void* reserved) {/ / initialize JVM (just save the virtual machine) / / then associate to the current thread fml::jni::InitJavaVM (vm); JNIEnv* env = fml::jni::AttachCurrentThread (); bool result = false; / / register FlutterMain. Result = flutter::FlutterMain::Register (env); FML_CHECK (result); / / Register PlatformView / / A large number of methods will be registered here so that C++ and java can call each other result = flutter::PlatformViewAndroid::Register (env); FML_CHECK (result); / / register VSyncWaiter. / / by mapping the method in the VSyncWaiter class of java with the VsyncWaiterAndroid in / / native, you can call each other result = flutter::VsyncWaiterAndroid::Register (env); FML_CHECK (result); return JNI_VERSION_1_4;} tip:PlatformViewAndroid path is: engine/shell/platform/android
If you are interested, you can take a look.
On the whole, the jvm is saved and the methods of C++ and java are mapped so that the two sides can call each other.
At this point, the native code pulled up in FlutterApplication is briefly summarized, and we follow the native code called in FlutterActivity.
FlutterActivity & onCreate
It is important to mention here that when you search for the class FlutterActivity, you will find two:
Android/FlutterActivity, this is the latest app/FlutterActivity. It has expired.
After a series of calls, see this article: analysis of the startup process of Flutter-- on the Android platform
The flutterEngine is initialized with the following constructor:
/ / very long, but the initialization is quite useful / / so I think it is necessary to post / * * Fully configurable {@ code FlutterEngine} constructor. * / public FlutterEngine (@ 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 ()) / / notice 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 ();}}
The entire constructor initializes a large number of channel and registers some native methods, where:
FlutterLoader.ensureInitializationComplete (context, dartVmArgs)
Go to native. The detailed code is as follows:
/ / this method blocks until the native system finishes executing 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 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") }} / / pull up native FlutterJNI.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);}}
Here, the relevant information will be passed through FlutterJNI.nativeInit, that is:
/ / native method public static native void nativeInit (@ NonNull Context context, @ NonNull String [] args, @ Nullable String bundlePath, @ NonNull String appStoragePath, @ NonNull String engineCachesPath, long initTimeMillis)
Pass it 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 [] = {{/ look here name is the meaning of the method name. Name = "nativeInit", .signature = "(Landroid/content/Context; [Ljava/lang/String;Ljava/"lang/String;Ljava/lang/String;Ljava/lang/String" J) V ", / / method & the address of Init is saved on fnPtr. FnPtr = reinterpret_cast (& Init),}, {.name =" nativePrefetchDefaultFontManager ", .signature =" () V ", .fnPtr = reinterpret_cast (& PrefetchDefaultFontManager),},} Jclass clazz = env- > FindClass ("io/flutter/embedding/engine/FlutterJNI"); if (clazz = = nullptr) {return false;} return env- > RegisterNatives (clazz, methods, fml::size (methods)) = = 0;}
With the pointer. FnPtr = reinterpret_cast (& Init), its FlutterMain::Init method is pulled.
Void FlutterMain::Init (JNIEnv* env, jclass clazz, jobject context, jobjectArray jargs, jstring kernelPath, jstring appStoragePath, jstring engineCachesPath, jlong initTimeMillis) {std::vector args / / tag args.push_back ("flutter"); / / save the path information we collected above to args / send it to java with'j'. 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); / / engine startup time 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)); / / initialize the cache path fml::paths::InitializeAndroidCachesPath (fml::jni::JavaStringToString (env, engineCachesPath)); / / load cache 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 (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); SetupObservatoryUriCallback flutterized mainline-> main (env);}
The above is mainly to save the data transmitted by java, so that the native caused by flutterLoader.ensureInitializationComplete is finished, and attachToJni () will be executed later.
FlutterActivity& attachToJni ()
AttachToJni () will eventually call flutterJNI.attachToNative (false):
/ / after this step is completed, android can communicate with engine @ UiThread public void attachToNative (boolean isBackgroundView) {ensureRunningOnMainThread (); ensureNotAttachedToNative (); nativePlatformViewId = nativeAttach (this, isBackgroundView);} private native long nativeAttach (@ NonNull FlutterJNI flutterJNI, boolean isBackgroundView)
This method calls the native:
Static jlong AttachJNI (JNIEnv* env, jclass clazz, jobject flutterJNI, jboolean is_background_view) {fml::jni::JavaObjectWeakGlobalRef java_object (env, flutterJNI); std::shared_ptr jni_facade = std::make_shared (java_object) / mainly initialize a shell holder auto shell_holder = std::make_unique (FlutterMain::Get (). GetSettings (), jni_facade, is_background_view); if (shell_holder- > IsValid ()) {return reinterpret_cast (shell_holder.release ());} else {return 0;}}
Let's take a look at the implementation of AndroidShellHolder.cc
AndroidShellHolder & the creation of gpu/ui/io threads
It has a constructor with more than 100 lines:
AndroidShellHolder::AndroidShellHolder (flutter::Settings settings, std::shared_ptr 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) / / here we pass false if (is_background_view) {thread_host_ = {thread_label, ThreadHost::Type::UI};} else {/ will create three threads: UI\ GPU\ IO thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU | ThreadHost::Type::IO} } / / Detach from JNI when the UI and raster threads exit. / / UI and pthread_setspecific thread exit, separate from JNI / / raster is the gpu thread, which converts our drawing instruction into gpu instruction auto jni_exit_task ([key = thread_destruct_key_] () {FML_CHECK (pthread_setspecific (key, reinterpret_cast (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 weak_platform_view; Shell::CreateCallback on_create_platform_view = [is_background_view, & jni_facade, & weak_platform_view] (Shell& shell) {std::unique_ptr platform_view_android If (is_background_view) {... Go below} else {/ initialize a PlatformViewAndroid platform_view_android = std::make_unique (shell, / / 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 on_create_rasterizer = [] (Shell& shell) {return std::make_unique (shell);} / / The current thread will be used as the platform thread. Ensure that the / / message loop is initialized. / / initialize the message loop / / gpu/ui/io of native they also have their own msg loop fml::MessageLoop::EnsureInitializedForCurrentThread (); / / initialize the task runner / of the corresponding thread so that we can post the task fml::RefPtr gpu_runner; fml::RefPtr ui_runner; fml::RefPtr io_runner; fml::RefPtr platform_runner = fml::MessageLoop::GetCurrent () .GetTaskRunner () to the specified thread. 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) / / increase ui and gpu thread levels / the smaller the thread value, the higher the thread level task_runners.GetRasterTaskRunner ()-> PostTask ([] () {/ / Android describes-8 as "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) PostTask ([] () {if (:: setpriority (PRIO_PROCESS, gettid (),-1)! = 0) {FML_LOG (ERROR) GetIsolateSnapshot (), / / isolate snapshot on_create_platform_view, / / on_create_rasterizer / / std::move (vm) / /) }
DartVMRef::Create
DartVMRef DartVMRef::Create (Settings settings, fml::RefPtr vm_snapshot, fml::RefPtr isolate_snapshot) {std::scoped_lock lifecycle_lock (gVMMutex);... Delete some code / / here to reuse the existing virtual machine if (auto vm = gVM.lock ()) {FML_DLOG (WARNING) GetSnapshotDelegate (); rasterizer_promise.set_value (std::move (rasterizer));}); / / create platform view in the current thread (platform thread). Auto platform_view = on_create_platform_view (* shell.get ()); 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 part of the code. / / create an io manager fml::TaskRunner::RunNowOrPostTask (io_task_runner, [& io_manager_promise, / / & weak_io_manager_promise, / / & unref_queue_promise) by post task to the io thread / / platform_view = platform_view- > GetWeakPtr (), / / io_task_runner, / / is_backgrounded_sync_switch = shell- > GetIsGpuDisabledSyncSwitch () / /] () {TRACE_EVENT0 ("flutter" "ShellSetupIOSubsystem") Auto io_manager = std::make_unique (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 (); / / create engine in UI thread / / the engine pointer here is used across threads using std::promise 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 (); / / create animator (ui thread) auto animator = std::make_unique (* shell, task_runners, std::move (vsync_waiter)) Engine_promise.set_value (std::make_unique (* 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;}
Because the code is too long, let's summarize it again:
After shell initialization, the following objects are also created in the io/ui/gpu/ platform thread:
Rasterizer works on the GPU thread and is responsible for converting drawing instructions into gpu instructions
Platform view in the current thread (platform thread)
Engine and animator work on UI threads
Flutter will eventually apply for VSync signal from platformView through animator.
Next let's take a look at the initialization of engine in the above code.
Engine
There is relatively little code, but there are a lot of things to connect:
Engine::Engine (Delegate& delegate, const PointerDataDispatcherMaker& dispatcher_maker, DartVM& vm, fml::RefPtr isolate_snapshot, TaskRunners task_runners, const PlatformData platform_data, Settings settings, std::unique_ptr animator, fml::WeakPtr io_manager Fml::RefPtr unref_queue, fml::WeakPtr snapshot_delegate): Engine (delegate, dispatcher_maker, vm.GetConcurrentWorkerTaskRunner (), task_runners, settings, std::move (animator), io_manager Nullptr) {runtime_controller_ = std::make_unique (* 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) }
Is to create a runtime_controller_, you can think of runtime controller as a link between native, platform and flutter.
Summary
After the above series of code, it may be a little dizzy. Generally speaking, Flutter Activity will create an initialization shell in the process of registering flutterMain, and in the process of initialization, we will create three threads, including the current thread, that is, four:
Platform thread (current thread)
Ui thread
Gpu thread
Io thread
And initialize a series of important objects.
OK, let's go back to the main line, onCreate () is over, so let's take a look at the onStart () life cycle:
FlutterActivity & onStart
This method calls delegate.onStart (); and finally calls the native method of FlutterJNI:
Private native void nativeRunBundleAndSnapshotFromLibrary (long nativePlatformViewId, @ NonNull String bundlePath, @ Nullable String entrypointFunctionName, @ Nullable String pathToEntrypointFunction, @ NonNull AssetManager manager)
From here, the end point is to execute the code for dart.
Launch
Then let's take a look at the native method:
(located in platform_view_android_jni_impl.cc)
Static void RunBundleAndSnapshotFromLibrary (JNIEnv* env, jobject jcaller, jlong shell_holder, jstring jBundlePath, jstring jEntrypoint Jstring jLibraryUrl, jobject jAssetManager) {... Delete part of the code / here is mainly based on the parameters to generate a config and used to start / our default startup entry 'main ()' is in this config ANDROID_SHELL_HOLDER- > Launch (std::move (config));}
Let's move on to Launch (std::move (config)); method:
Void AndroidShellHolder::Launch (RunConfiguration config) {if (! IsValid ()) {return;} shell_- > RunEngine (std::move (config);}
The run engine method is also called:
Void Shell::RunEngine (RunConfiguration run_configuration, const std::function& result_callback) {... Delete some code / post a task to the ui thread 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) Run (std::move (run_configuration)) If (run_result = = flutter::Engine::RunStatus::Failure) {FML_LOG (ERROR)
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.