首页 > 解决方案 > 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()被调用会很棒。

标签: androidiosflutterdart

解决方案


在 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.storyboardInfo.plist

Xcode 的 Interface Builder 的屏幕截图,其中 Main.storyboard 打开,身份检查器打开,将 FlutterViewController 显示为自定义类。

无论如何,当 iOS 创建故事板时,该window属性是在 AppDelegate 上设置的。您可以FlutterViewController在 AppDelegate 中使用window.rootViewController. 一个 Objective-C++ 文件,FlutterViewController.mmsharedSetupWithProject方法创建一个FlutterEngineusing [[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.mmlaunchEngine调用,入口点(你的飞镖的主要功能。)

- (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方法。因为MainActivityextends 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.
 *

稍后, inFlutterActivityAndFragmentDelegatedoInitialFlutterViewRun调用,它再次创建一个基于 FlutterActivity 的DartEntryPointmain方法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);被调用。因此,您的主要方法已“被调用”。但这使用了FlutterJNIFlutterEngine 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),
      },

RunBundleAndSnapshotFromLibraryC++ 方法在哪里:

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被称为。


推荐阅读