android - Flutter 的 main() 方法何时/何地从 Android 和 iOS 端调用?
问题描述
如果我理解正确:当我们运行使用 Flutter 构建的 Android 应用程序时,它首先会转到AndroidManfiest.xml
文件,查找LAUNCHER
活动并启动它。默认情况下,此活动MainActivity.kt
扩展FlutterActivity
。
但是应用程序的 Flutter 部分在调用main()
in 方法时开始main.dart
。
我的问题是,谁调用这个main()
方法?
对于Android,它MainActivity
是扩展的FlutterActivity
吗?还是本身有一些逻辑FlutterActivity
?还是完全有其他机制,而我完全错过了重点?
同样的问题也适用于 iOS,FlutterViewController
而不是FlutterActivity
.
一个指向源代码的链接,只是澄清什么时候main()
被调用会很棒。
解决方案
在 iOS 上
main.m
在 Flutter 项目目录中自动生成了一个文件ios/Runner
,该文件定义了C 程序实现的常规 main 函数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]));
}
}
它在Swift中更简单,只需AppDelegate
用@UIApplicationMain
.
AppDelegate 是启动 Flutter 的类,因为它扩展了FlutterAppDelegate
. 当FlutterAppDelegate
被实例化时,iOS 会创建FlutterViewController
,它会创建一个 FlutterEngine。它创建FlutterViewController
是因为已在应用程序中指定了 中的FlutterViewController
配置。所以从技术上讲,Flutter 应用程序是Storyboard应用程序。Main.storyboard
Info.plist
无论如何,当 iOS 创建故事板时,该window
属性是在 AppDelegate 上设置的。您可以FlutterViewController
在 AppDelegate 中使用window.rootViewController
. 一个 Objective-C++ 文件,FlutterViewController.mm
的sharedSetupWithProject
方法创建一个FlutterEngine
using [[FlutterEngine alloc]initWithName:...
:
- (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];
}
最终, inFlutterEngine.mm
被launchEngine
调用,入口点(你的飞镖的主要功能。)
- (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
是在 shell.cc中实现的 C++ 函数。我现在要停在那里。这可能是事件循环开始的地方,使用platform_runner->PostTask(...)
.
在安卓上
在生成的 Flutter 项目的 android 目录中, MainActivity
声明为AndroidManifest.xml
从主屏幕启动的应用程序。
当活动启动时,Android 将调用活动的onCreate
方法。因为MainActivity
extends FlutterActivity
,所以调用了这个onCreate
方法。FlutterActivityAndFragmentDelegate
被实例化,并onAttach
调用它的方法。最终,使用以下命令创建 Java 表示FlutterEngine
:
flutterEngine =
new FlutterEngine(
host.getContext(),
host.getFlutterShellArgs().toArray(),
/*automaticallyRegisterPlugins=*/ false,
/*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
这使用FlutterJNI
.
/** * 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. *
稍后, inFlutterActivityAndFragmentDelegate
被doInitialFlutterViewRun
调用,它再次创建一个基于 FlutterActivity 的DartEntryPoint
main方法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
被称为。
推荐阅读
- c# - 如何使用随机数生成器中的值显示嵌入程序中的图像?
- vue.js - 如何使用 bootstrap-vue 修复模态标题中的图像
- javascript - symfony 列表上的内联编辑元素问题
- text - 生成的 Beautiful Soup txt 文件中的文件编码未知
- typescript - 基于可选泛型的接口强制键
- vue.js - v-container 不会左对齐
- r - 将 .RDS 文件存储在 github 存储库中
- linkedin - LinkedIn:营销开发平台陷入审查流程
- windows - 如何获取文件夹中第二个最新文件的文件名?
- swagger - 如何使用 Swagger 和 Spring Boot 进行版本控制?