Flutter 的 main() 方法何时/何地从 Android 和 iOS 端调用?
Posted
技术标签:
【中文标题】Flutter 的 main() 方法何时/何地从 Android 和 iOS 端调用?【英文标题】:When/Where is Flutter's main() method called from the Android and iOS side of things? 【发布时间】:2021-08-10 14:13:19 【问题描述】:如果我理解正确:
当我们运行一个使用 Flutter 构建的 android 应用程序时,它首先会转到 AndroidManfiest.xml
文件,查找 LAUNCHER
活动并启动它。此活动默认为MainActivity.kt
,扩展FlutterActivity
。
但应用程序的 Flutter 部分从调用 main.dart
中的 main()
方法开始。
我的问题是,谁调用了这个main()
方法?
对于 Android,是 MainActivity
扩展 FlutterActivity
吗?还是FlutterActivity
本身有一些逻辑?还是完全有其他机制,我完全没有抓住重点?
同样的问题也适用于使用FlutterViewController
而不是FlutterActivity
的ios。
一个指向此源代码的链接,它只是阐明何时调用main()
会很棒。
【问题讨论】:
Dart 运行时(或调试版本中的 VM)执行main
。 Dart 运行时/VM 由 Flutter 引擎启动。
好的。你能指出在调试版本中触发 Dart VM 的代码吗?当我从 xcode 运行 iOS 应用程序时,必须在某些特定于 iOS 的文件中进行一些配置,这些配置的作用与 Swift/Obj-C iOS 应用程序不同,即触发这个 Dart VM。
我不记得确切的细节,但我认为您必须查看flutter_engine 中的 C++ 和 Objective-C 代码,例如FlutterDartProject.mm
.
完美,这正是我好奇的地方。谢谢!在此处记录直接链接以供进一步阅读:iOS:github.com/flutter/engine/blob/… Android:github.com/flutter/engine/blob/…
【参考方案1】:
在 iOS 上
在您的 Flutter 项目 ios/Runner
目录中自动生成了一个 main.m
文件,该文件定义了 C program implements、int main(int argc, char* argv[]);
的常规主函数。
一个编译输出只能有一个main方法,当程序启动时编译器会立即运行。以下代码创建了一个UIApplicationMain
,它“创建应用程序对象和应用程序委托并设置事件循环”:
#import "AppDelegate.h"
int main(int argc, char* argv[])
@autoreleasepool
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
它是simpler in Swift,只需将AppDelegate
注释为@UIApplicationMain
。
AppDelegate 是启动 Flutter 的类,因为它扩展了 FlutterAppDelegate
。当FlutterAppDelegate
被实例化时,iOS会创建FlutterViewController
,它会创建一个FlutterEngine。它创建FlutterViewController
,因为FlutterViewController
是在Main.storyboard
中配置的,而Info.plist
已在应用程序中指定。所以从技术上讲,Flutter 应用程序是 Storyboard 应用程序?。
无论如何,当 iOS 创建故事板时,AppDelegate 上会设置 window
属性。您可以使用window.rootViewController
在 AppDelegate 中获取FlutterViewController
。一个Objective-C++文件,FlutterViewController.mm
的sharedSetupWithProject
方法使用[[FlutterEngine alloc]initWithName:...
创建了一个FlutterEngine
:
- (void)sharedSetupWithProject:(nullable FlutterDartProject*)project
initialRoute:(nullable NSString*)initialRoute
// Need the project to get settings for the view. Initializing it here means
// the Engine class won't initialize it later.
if (!project)
project = [[[FlutterDartProject alloc] init] autorelease];
FlutterView.forceSoftwareRendering = project.settings.enable_software_rendering;
auto engine = fml::scoped_nsobject<FlutterEngine>[[FlutterEngine alloc]
initWithName:@"io.flutter"
project:project
allowHeadlessExecution:self.engineAllowHeadlessExecution
restorationEnabled:[self restorationIdentifier] != nil];
if (!engine)
return;
_viewOpaque = YES;
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
_engine = std::move(engine);
_flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
[_engine.get() createShell:nil libraryURI:nil initialRoute:initialRoute];
_engineNeedsLaunch = YES;
_ongoingTouches.reset([[NSMutableSet alloc] init]);
[self loadDefaultSplashScreenView];
[self performCommonViewControllerInitialization];
最终,在FlutterEngine.mm
中,launchEngine
被调用,并带有入口点(dart 的主函数。)
- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil
// Launch the Dart application with the inferred run configuration.
self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryOrNil]);
Shell::RunEngine
是实现in shell.cc 的 C++ 函数。我现在要停在那里。这可能是事件循环开始的地方?,使用platform_runner->PostTask(...)
。
在安卓上
在生成的 Flutter 项目的 android 目录中,MainActivity
在 AndroidManifest.xml
中声明为从主屏幕启动的应用程序。
当activity启动时,Android会调用activity的onCreate
方法。因为MainActivity
扩展了FlutterActivity
,所以调用了这个onCreate
方法。 FlutterActivityAndFragmentDelegate
被实例化,它的onAttach
方法被调用。最终,FlutterEngine
的 Java 表示使用以下命令创建:
flutterEngine =
new FlutterEngine(
host.getContext(),
host.getFlutterShellArgs().toArray(),
/*automaticallyRegisterPlugins=*/ false,
/*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
这使用FlutterJNI
与 C++ FlutterEngine 库进行通信。
/** * Interface between Flutter embedding's Java code and Flutter engine's C/C++ code. * * <p>Flutter's engine is built with C/C++. The Android Flutter embedding is responsible for * coordinating Android OS events and app user interactions with the C/C++ engine. Such coordination * requires messaging from an Android app in Java code to the C/C++ engine code. This communication * requires a JNI (Java Native Interface) API to cross the Java/native boundary. *
稍后,在FlutterActivityAndFragmentDelegate
中,doInitialFlutterViewRun
被调用,这会创建一个DartEntryPoint
,你的main方法再次,基于FlutterActivity的ActivityInfo
。这将使用以下内容获取入口函数名称,但默认为"main"
:
@NonNull
public String getDartEntrypointFunctionName()
try
Bundle metaData = getMetaData();
String desiredDartEntrypoint =
metaData != null ? metaData.getString(DART_ENTRYPOINT_META_DATA_KEY) : null;
return desiredDartEntrypoint != null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT;
catch (PackageManager.NameNotFoundException e)
return DEFAULT_DART_ENTRYPOINT;
然后,flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
被调用。因此,您的主要方法已“被调用”。但这使用了FlutterJNI
和 FlutterEngine C++ 代码。首先调用flutterJNI.runBundleAndSnapshotFromLibrary
,最后调用这个JNI原生方法:
private native void nativeRunBundleAndSnapshotFromLibrary(
long nativeShellHolderId,
@NonNull String bundlePath,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager manager);
这个原生方法定义在platform_view_adroid_jni_impl.cc
:
.name = "nativeRunBundleAndSnapshotFromLibrary",
.signature = "(JLjava/lang/String;Ljava/lang/String;"
"Ljava/lang/String;Landroid/content/res/AssetManager;)V",
.fnPtr = reinterpret_cast<void*>(&RunBundleAndSnapshotFromLibrary),
,
RunBundleAndSnapshotFromLibrary
是 C++ 方法:
static void RunBundleAndSnapshotFromLibrary(JNIEnv* env,
jobject jcaller,
jlong shell_holder,
jstring jBundlePath,
jstring jEntrypoint,
jstring jLibraryUrl,
jobject jAssetManager)
auto asset_manager = std::make_shared<flutter::AssetManager>();
asset_manager->PushBack(std::make_unique<flutter::APKAssetProvider>(
env, // jni environment
jAssetManager, // asset manager
fml::jni::JavaStringToString(env, jBundlePath)) // apk asset dir
);
auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint);
auto libraryUrl = fml::jni::JavaStringToString(env, jLibraryUrl);
ANDROID_SHELL_HOLDER->Launch(asset_manager, entrypoint, libraryUrl);
AndroidShellHolder::Launch
在哪里:
void AndroidShellHolder::Launch(std::shared_ptr<AssetManager> asset_manager,
const std::string& entrypoint,
const std::string& libraryUrl)
if (!IsValid())
return;
asset_manager_ = asset_manager;
auto config = BuildRunConfiguration(asset_manager, entrypoint, libraryUrl);
if (!config)
return;
shell_->RunEngine(std::move(config.value()));
就像 iOS 一样,Shell::RunEngine
被调用。
【讨论】:
以上是关于Flutter 的 main() 方法何时/何地从 Android 和 iOS 端调用?的主要内容,如果未能解决你的问题,请参考以下文章
BaseHTTPServer和SimpleHTTPServer有什么区别?何时何地使用它们?