【问题标题】:When/Where is Flutter's main() method called from the Android and iOS side of things?Flutter 的 main() 方法何时/何地从 Android 和 iOS 端调用?
【发布时间】: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/…

标签: android ios flutter dart


【解决方案1】:

在 iOS 上

在您的 Flutter 项目 ios/Runner 目录中自动生成了一个 main.m 文件,该文件定义了 C program implementsint 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.mmsharedSetupWithProject方法使用[[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-&gt;PostTask(...)


在安卓上

在生成的 Flutter 项目的 android 目录中,MainActivityAndroidManifest.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 被调用。

【讨论】:

    猜你喜欢
    • 2021-12-21
    • 2017-02-13
    • 2013-11-28
    • 2019-04-21
    • 2014-06-09
    • 2013-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多