android启动一个Flutter页面都经历了哪些?

966 阅读3分钟

在 Android 原生应用中启动一个 Flutter 页面时,会涉及到多个步骤和组件,包括 FlutterEngine 的初始化、FlutterActivity 的启动、Dart 代码的执行等。下面将详细描述从 Android 端启动 Flutter 页面的完整流程,并从源码的角度进行分析。

1. FlutterEngine 初始化

FlutterEngine 是 Flutter 应用的引擎,主要负责执行 Dart 代码,并将其输出到 UI 界面中。

kotlin
复制代码
val flutterEngine = FlutterEngine(context)
flutterEngine.dartExecutor.executeDartEntrypoint(
    DartExecutor.DartEntrypoint.createDefault()
)

核心实现

java
复制代码
public FlutterEngine(@NonNull Context context, @NonNull FlutterLoader flutterLoader) {
    this.dartExecutor = new DartExecutor(flutterLoader, flutterJNI);
    flutterLoader.startInitialization(context.getApplicationContext());
    flutterLoader.ensureInitializationComplete(context.getApplicationContext(), null);
    this.pluginRegistry = new FlutterEnginePluginRegistry(this, dartExecutor);
    this.dartExecutor.executeDartEntrypoint(
        DartExecutor.DartEntrypoint.createDefault()
    );
}

在初始化过程中,FlutterEngine 加载了 DartExecutor,并通过 DartExecutor 运行 Dart 代码的入口点(main 函数)。同时,插件注册表(pluginRegistry)也会被初始化,用于管理 Flutter 中的插件。

2. FlutterActivity 的启动

FlutterActivity 是一个承载 Flutter 界面的 Android Activity。在启动时,它会绑定一个 FlutterEngine 来渲染 Flutter 界面。

kotlin
复制代码
val intent = FlutterActivity.withCachedEngine("my_engine_id").build(context)
context.startActivity(intent)

核心实现

java
复制代码
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    FlutterEngine flutterEngine = getFlutterEngine();

    if (flutterEngine == null) {
        flutterEngine = new FlutterEngine(this);
    }

    setContentView(createFlutterView(flutterEngine));
}

onCreate 方法中,FlutterActivity 检查是否有缓存的 FlutterEngine,如果没有则创建一个新的引擎实例,并将其与 FlutterView 绑定。

3. FlutterView 的渲染

FlutterView 是实际渲染 Flutter 界面的 View 组件。

kotlin
复制代码
FlutterView flutterView = new FlutterView(this);
flutterView.attachToFlutterEngine(flutterEngine);
setContentView(flutterView);

核心实现

java
复制代码
public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    flutterRenderer.attachToRenderer(flutterEngine.getRenderer());
    flutterSurfaceView.attachToRenderer(flutterRenderer);
}

FlutterView 通过 attachToFlutterEngine 方法将 FlutterEngine 与 FlutterView 绑定,确保 Dart 代码生成的 UI 能够正确地渲染在 Android 界面上。

4. MethodChannel 的使用

MethodChannel 是 Flutter 与 Android 原生代码之间通信的通道。它允许 Dart 代码调用 Android 方法,也允许 Android 代码调用 Dart 方法。

kotlin
复制代码
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.channel").setMethodCallHandler { call, result ->
    when (call.method) {
        "methodName" -> {
            result.success("方法调用成功")
        }
        else -> {
            result.notImplemented()
        }
    }
}

核心实现

java
复制代码
public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHandler handler) {
    binaryMessenger.setMessageHandler(channel, handler);
}

MethodChannel 的消息处理依赖于 DartExecutor 的 binaryMessenger。这个消息机制通过 JNI 与 Dart 代码进行通信,确保方法调用能够跨语言执行。

5. Dart Isolate 启动

DartExecutor 负责启动 Dart Isolate,并执行 Dart 代码。

java
复制代码
public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) {
    flutterJNI.runBundleAndSnapshotFromLibrary(
        dartEntrypoint.pathToBundle,
        dartEntrypoint.dartEntrypointFunctionName,
        dartEntrypoint.androidAssetManager
    );
}

Dart Isolate 是 Dart 运行时的执行单元,DartExecutor 通过 JNI 调用原生方法启动 Dart Isolate,并运行指定的 Dart 代码。

6. UI 渲染与事件处理

Flutter 的 UI 渲染过程依赖于 DartExecutor 将 Dart UI 树转换为渲染指令,然后通过 FlutterJNI 传递给底层的渲染器。

java
复制代码
public void onSurfaceCreated(@NonNull Surface surface) {
    flutterJNI.onSurfaceCreated(surface);
}

public void onDrawFrame() {
    flutterJNI.onDrawFrame();
}

FlutterJNI 处理了与 C++ 层的渲染器之间的交互,确保 Dart 生成的 UI 能够在 OpenGL 上进行渲染。

7. FlutterEngine 销毁与资源回收

当一个 FlutterEngine 不再需要时,可以调用 destroy 方法来释放其所有资源。

kotlin
复制代码
flutterEngine.destroy()

核心实现

java
复制代码
public void destroy() {
    dartExecutor.stop();
    pluginRegistry.destroy();
    flutterRenderer.stopRenderingToSurface();
}

destroy 方法中,FlutterEngine 停止了 DartExecutor,销毁了插件注册表,并停止了渲染操作,确保没有资源泄漏。

如果你在项目中使用了多个 FlutterEngine,则需要通过唯一的标识符来管理 MethodChannel,以确保每个引擎实例都能够正确地处理消息。这种情况下,可以考虑使用工厂模式或单例模式来管理 MethodChannel 实例,或者将 MethodChannel 的实例化逻辑放在引擎初始化的过程中,以便每个引擎拥有独立的消息通道。

此外,如果项目中已经有继承关系,无法直接继承 FlutterActivity,你可以使用 FlutterFragment 或者通过组合方式将 FlutterEngine 嵌入到现有的 Activity 中。这种方式可以让你更灵活地处理 Flutter 和原生的交互,而不影响原有的继承结构。

总结

启动 Flutter 页面涉及从 FlutterEngine 的初始化、FlutterActivity 的启动、Dart 代码的执行到 Flutter UI 渲染的完整流程。理解这些过程有助于优化 Flutter 页面启动性能,排查 Flutter 页面与原生系统交互中的问题,并为定制 Flutter 应用提供了深入的支持。