关于flutter:Flutter-Android-端-FlutterEngine-Java-相关流程源码分析

40次阅读

共计 12537 个字符,预计需要花费 32 分钟才能阅读完成。

Flutter 系列文章连载~

  • 《Flutter Android 工程构造及应用层编译源码深入分析》
  • 《Flutter 命令实质之 Flutter tools 机制源码深入分析》
  • 《Flutter 的 runApp 与三棵树诞生流程源码剖析》
  • 《Flutter Android 端 Activity/Fragment 流程源码剖析》
  • 《Flutter Android 端 FlutterInjector 及依赖流程源码剖析》
  • 《Flutter Android 端 FlutterEngine Java 相干流程源码剖析》
  • 《Flutter Android 端 FlutterView 相干流程源码剖析》
  • 《Flutter 绘制动机 VSYNC 流程源码全方位剖析》
  • 《Flutter 安卓 Platform 与 Dart 端音讯通信形式 Channel 源码解析》

背景

咱们在 Flutter Android 端的 Java 层代码中常常看到 FlutterEngine、FlutterEngineGroup、FlutterEngineCache 等相干类的应用,你是不是也常常搞不清他们的关系和作用?本文就是对他们的一个解剖剖析,因为 Flutter 2 版本对这块做了大调整,所以咱们的剖析以 2.2.3 版本为例剖析。

FlutterEngine 相干剖析

FlutterEngine 是一个独立的 Flutter 运行环境容器,通过它能够在 Android 应用程序中运行 Dart 代码。FlutterEngine 中的 Dart 代码能够在后盾执行,也能够应用附带的 FlutterRenderer 和 Dart 代码将 Dart 端 UI 成果渲染到屏幕上,渲染能够开始和进行,从而容许 FlutterEngine 从 UI 交互转移到仅进行数据处理,而后又返回到 UI 交互的能力。

应用 FlutterEngine 执行 Dart 或 Flutter 代码须要先通过 FlutterEngine 获取 DartExecutor 援用,而后调用 DartExecutor 的 executeDartEntrypoint(DartExecutor.DartEntrypoint) 执行 Dart 代码即可,同一个 FlutterEngine 实例中获取的 DartExecutor 的 executeDartEntrypoint(DartExecutor.DartEntrypoint) 办法只能被调用一次,切记。

想要把 Flutter 内容渲染到屏幕上,须要调用 FlutterEngine 的 getRenderer() 办法获取一个 FlutterRenderer 援用,而后让 FlutterRenderer 实例 attach 上一个 RenderSurface(譬如默认提供的 FlutterView,也即其外部的 FlutterSurfaceView、FlutterTextureView、FlutterImageView 之一,参见后面系列文章)。

App 每个过程中创立第一个 FlutterEngine 实例的时候会加载 Flutter 引擎的原生库并启动 Dart VM(VM 存活生命周期追随过程),随后同过程中其余的 FlutterEngines 将在同一个 VM 实例上运行,但在运行 DartExecutor 时将领有本人的 Dart Isolate。每个 Isolate 都是一个独立的 Dart 环境,除非通过 Isolate 端口,否则无奈互相通信。[参见官网文档]

所以,对于一个多过程且多 FlutterEngine 的 app 来说,其 FlutterEngine 与 DartExecutor、Dart VM、Isolate 的关系大抵如下图:

上面是 FlutterEngine 外围源码片段:

public class FlutterEngine {
  //Flutter C/C++ 与平台 java 层接口定义交互。@NonNull private final FlutterJNI flutterJNI;
  // 用来把 Flutter Dart UI 渲染到屏幕上,renderer 会 attach 到 RenderSurface 上。@NonNull private final FlutterRenderer renderer;
  //Dart 执行器。@NonNull private final DartExecutor dartExecutor;
  // 用来治理安卓组件和 Flutter plugins 插件。@NonNull private final FlutterEngineConnectionRegistry pluginRegistry;
  //localization 的安卓端实现插件。@NonNull private final LocalizationPlugin localizationPlugin;

  // 一堆零碎通道。@NonNull private final AccessibilityChannel accessibilityChannel;
  @NonNull private final DeferredComponentChannel deferredComponentChannel;
  @NonNull private final KeyEventChannel keyEventChannel;
  @NonNull private final LifecycleChannel lifecycleChannel;
  @NonNull private final LocalizationChannel localizationChannel;
  @NonNull private final MouseCursorChannel mouseCursorChannel;
  @NonNull private final NavigationChannel navigationChannel;
  @NonNull private final RestorationChannel restorationChannel;
  @NonNull private final PlatformChannel platformChannel;
  @NonNull private final SettingsChannel settingsChannel;
  @NonNull private final SystemChannel systemChannel;
  @NonNull private final TextInputChannel textInputChannel;

  // Platform Views.
  @NonNull private final PlatformViewsController platformViewsController;
  // Engine Lifecycle.
  @NonNull private final Set<EngineLifecycleListener> engineLifecycleListeners = new HashSet<>();
  //......

  // 全参数的构造函数,各种结构最终都走进这里
  public FlutterEngine(
      @NonNull Context context,
      @Nullable FlutterLoader flutterLoader,
      @NonNull FlutterJNI flutterJNI,
      @NonNull PlatformViewsController platformViewsController,
      @Nullable String[] dartVmArgs,
      boolean automaticallyRegisterPlugins,
      boolean waitForRestorationData) {
    //......
    // 创立一个 DartExecutor 并将 flutterJNI 和安卓平台的 assetManager 实例传递进去。this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
    this.dartExecutor.onAttachedToJNI();
    //......
    // 各种 channel 实例化,上面独立剖析。accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
    deferredComponentChannel = new DeferredComponentChannel(dartExecutor);
    keyEventChannel = new KeyEventChannel(dartExecutor);
    lifecycleChannel = new LifecycleChannel(dartExecutor);
    localizationChannel = new LocalizationChannel(dartExecutor);
    mouseCursorChannel = new MouseCursorChannel(dartExecutor);
    navigationChannel = new NavigationChannel(dartExecutor);
    platformChannel = new PlatformChannel(dartExecutor);
    restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData);
    settingsChannel = new SettingsChannel(dartExecutor);
    systemChannel = new SystemChannel(dartExecutor);
    textInputChannel = new TextInputChannel(dartExecutor);
    //......
    // 插件实例化。this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);

    this.flutterJNI = flutterJNI;
    if (flutterLoader == null) {flutterLoader = FlutterInjector.instance().flutterLoader();}
    //......

    this.pluginRegistry =
        new FlutterEngineConnectionRegistry(context.getApplicationContext(), this, flutterLoader);
    // 默认就是主动注册 plugins 的,能够通过清单文件配置变更等。if (automaticallyRegisterPlugins && flutterLoader.automaticallyRegisterPlugins()) {registerPlugins();
    }
  }
  //......

  // 注册 flutter 我的项目根目录下 pubspec.yaml 中依赖的所有 flutter plugins。//Flutter tool 会生成一个 GeneratedPluginRegistrant 的类。private void registerPlugins() {
    try {Class<?> generatedPluginRegistrant = Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
      Method registrationMethod = generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
      registrationMethod.invoke(null, this);
    } catch (Exception e) {
      Log.w(TAG, "Tried to automatically register plugins with FlutterEngine ("
              + this + ") but could not find and invoke the GeneratedPluginRegistrant.");
    }
  }
  //...... 省略一堆属性成员的 get 办法
}

下面代码片段其实挺直观,次要就是负责各种插件、Channel、渲染环境、运行环境的实例化筹备工作。咱们重点看一下下面的 registerPlugins() 办法,他外部反射调用了 io.flutter.plugins.GeneratedPluginRegistrant 类的 registerWith(this) 办法把以后 FlutterEngine 实例传递进去。

你可能会问,这个 io.flutter.plugins.GeneratedPluginRegistrant 类是哪里来的呢?其实在 Flutter 我的项目根目录下 pubspec.yaml 文件的依赖中如果有 Flutter Plugin 则会在执行 flutter pub get 等 Flutter tools 命令时主动生成一个名为 GeneratedPluginRegistrant 的类,其中蕴含依赖的 Flutter Plugin 相干 add 代码。咱们以一个 demo 为例来进行阐明,如下图示在 pubspec.yaml 中追加了 webview_flutter 依赖,实质是一个 Flutter Plugin,运行 pub get 后的成果如下:

能够看到,在结构实例化 FlutterEngine 时会调用其 registerPlugins() 办法,registerPlugins()办法会反射调用主动生成的 io.flutter.plugins.GeneratedPluginRegistrant 类的 registerWith(this) 办法把以后 FlutterEngine 实例传递进去。而 io.flutter.plugins.GeneratedPluginRegistrant 类的 registerWith(this) 办法中次要就是将咱们在 pubspec.yaml 文件中的 Flutter Plugin 依赖追加到 Plugins 汇合中。

咱们先看下 flutterEngine.getPlugins().add(xxx) 办法:

class FlutterEngineConnectionRegistry
    implements PluginRegistry,
        ActivityControlSurface,
        ServiceControlSurface,
        BroadcastReceiverControlSurface,
        ContentProviderControlSurface {
  //......
  @Override
  public void add(@NonNull FlutterPlugin plugin) {
    //......
    plugins.put(plugin.getClass(), plugin);
    plugin.onAttachedToEngine(pluginBinding);

    // For ActivityAware plugins, add the plugin to our set of ActivityAware
    // plugins, and if this engine is currently attached to an Activity,
    // notify the ActivityAware plugin that it is now attached to an Activity.
    if (plugin instanceof ActivityAware) {ActivityAware activityAware = (ActivityAware) plugin;
      activityAwarePlugins.put(plugin.getClass(), activityAware);

      if (isAttachedToActivity()) {activityAware.onAttachedToActivity(activityPluginBinding);
      }
    }

    // For ServiceAware plugins, add the plugin to our set of ServiceAware
    // plugins, and if this engine is currently attached to a Service,
    // notify the ServiceAware plugin that it is now attached to a Service.
    if (plugin instanceof ServiceAware) {ServiceAware serviceAware = (ServiceAware) plugin;
      serviceAwarePlugins.put(plugin.getClass(), serviceAware);

      if (isAttachedToService()) {serviceAware.onAttachedToService(servicePluginBinding);
      }
    }

    // For BroadcastReceiverAware plugins, add the plugin to our set of BroadcastReceiverAware
    // plugins, and if this engine is currently attached to a BroadcastReceiver,
    // notify the BroadcastReceiverAware plugin that it is now attached to a BroadcastReceiver.
    if (plugin instanceof BroadcastReceiverAware) {BroadcastReceiverAware broadcastReceiverAware = (BroadcastReceiverAware) plugin;
      broadcastReceiverAwarePlugins.put(plugin.getClass(), broadcastReceiverAware);

      if (isAttachedToBroadcastReceiver()) {broadcastReceiverAware.onAttachedToBroadcastReceiver(broadcastReceiverPluginBinding);
      }
    }

    // For ContentProviderAware plugins, add the plugin to our set of ContentProviderAware
    // plugins, and if this engine is currently attached to a ContentProvider,
    // notify the ContentProviderAware plugin that it is now attached to a ContentProvider.
    if (plugin instanceof ContentProviderAware) {ContentProviderAware contentProviderAware = (ContentProviderAware) plugin;
      contentProviderAwarePlugins.put(plugin.getClass(), contentProviderAware);

      if (isAttachedToContentProvider()) {contentProviderAware.onAttachedToContentProvider(contentProviderPluginBinding);
      }
    }
  }
  //......
}

能够看到,FlutterEngineConnectionRegistry 的 add 办法不须要咱们做过多解释就能看懂,次要就是增加一个 FlutterPlugin 实例,而后调用 FlutterPlugin 接口约定的一堆相似生命周期办法,譬如 onAttachedToEngine,而后根据插件的具体类型(安卓平台组件类型,Activity、Service、Broadcast、ContentProvider)进行对应的办法调用,这样 Flutter Plugin 插件开发者就能根据这些机会办法进行本人的平台逻辑解决。譬如下面 demo 中 webview_flutter Flutter Plugin 源码中的实现,如下:

public class WebViewFlutterPlugin implements FlutterPlugin {
  private FlutterCookieManager flutterCookieManager;
  //......
  //FlutterEngineConnectionRegistry 的 add 中触发调用,实例化 BinaryMessenger 和 FlutterCookieManager。@Override
  public void onAttachedToEngine(FlutterPluginBinding binding) {BinaryMessenger messenger = binding.getBinaryMessenger();
    binding.getPlatformViewRegistry().registerViewFactory("plugins.flutter.io/webview", new WebViewFactory(messenger, /*containerView=*/ null));
    flutterCookieManager = new FlutterCookieManager(messenger);
  }
  
  //FlutterEngineConnectionRegistry 的 remove 中触发调用,移除插件。@Override
  public void onDetachedFromEngine(FlutterPluginBinding binding) {if (flutterCookieManager == null) {return;}
    flutterCookieManager.dispose();
    flutterCookieManager = null;
  }
}

这下搞懂每次编译 Flutter Android App 时主动生成的 GeneratedPluginRegistrant 是咋回事了吧,也晓得为啥要求 GeneratedPluginRegistrant 类须要在混同清单中被 keep 住的起因了吧。整体流程大抵如下图:

对于 FlutterEngine 构造函数中的各种实例化 Channel 咱们这里先不开展,前面独自篇章解析。

FlutterEngineCache 相干剖析

FlutterEngineCache 其实很简略,目标就是一个过程单例模式,其中通过 Map 存储缓存 FlutterEngine 实例,代码也没啥好剖析的。

public class FlutterEngineCache {
  private static FlutterEngineCache instance;
  // 单例模式
  public static FlutterEngineCache getInstance() {if (instance == null) {instance = new FlutterEngineCache();
    }
    return instance;
  }
  // 基于 key 缓存 FlutterEngine 实例汇合
  private final Map<String, FlutterEngine> cachedEngines = new HashMap<>();

  // 判断是否蕴含指定 id 的 FlutterEngine 实例
  public boolean contains(@NonNull String engineId) {return cachedEngines.containsKey(engineId);
  }

  // 获取指定 id 的 FlutterEngine 实例
  public FlutterEngine get(@NonNull String engineId) {return cachedEngines.get(engineId);
  }

  // 缓存指定 id 的 FlutterEngine 实例
  public void put(@NonNull String engineId, @Nullable FlutterEngine engine) {if (engine != null) {cachedEngines.put(engineId, engine);
    } else {cachedEngines.remove(engineId);
    }
  }

  // 删除指定 id 的 FlutterEngine 实例
  public void remove(@NonNull String engineId) {put(engineId, null);
  }

  // 清空整个 cache
  public void clear() {cachedEngines.clear();
  }
}

FlutterActivity 反对和缓存的 FlutterEngine 一起应用,能够通过 FlutterActivity.withCachedEngine(String) 构建一个 FlutterActivity Intent,该 Intent 配置为应用现有的缓存 FlutterEngine。应用缓存的 FlutterEngine 时,该 FlutterEngine 该当曾经执行 Dart 代码,也就是说 Dart 入口点和初始路由曾经定义,所以 CachedEngineIntentBuilder 不提供这些配置个性。举荐应用 FlutterEngineCache,这样做能够预热引擎,缩小启动 Flutter 页面时的白屏或者等待时间,就像官网说的一样。

FlutterEngineGroup 相干剖析

比拟早接触 Flutter 的小伙伴应该都晓得,Flutter 混合开发中,多个 Flutter 页面(FlutterActivity)模式最被诟病的问题之一就是会生成多个 FlutterEngine 实例且每个 FlutterEngine 实例十分占用内存,所以才有了民间相似咸鱼 Flutter 的 Flutter Boost 计划,采纳单 FlutterEngine 计划(分屏等场景无奈兼容)且整个单过程 App 在同一个 Isolate 下做到内存共享。起初因为社区呼声太高,官网也致力在 Flutter 2.0 公布了试验个性的 FlutterEngineGroup 多 FlutterEngine 官网解决方案,即每个页面是一个 FlutterEngine,或者一个页面内蕴含多个 FlutterEngine,每个 FlutterEngine 对应一个 Isolate 且内存不共享。官网说从 FlutterEngineGroup 生成的 FlutterEngine 内存只减少 180k,因为它对罕用资源进行共享(例如 GPU 上下文、字体度量和隔离线程的快照等),放慢首次渲染的速度、升高提早并升高内存占用。然而到目前 Flutter 2.2 版本为止,FlutterEngineGroup 仍旧处于试验个性阶段,不举荐在正式我的项目中应用,参见官网 multiple-flutters 文档。上面先看下外围源码:

public class FlutterEngineGroup {final List<FlutterEngine> activeEngines = new ArrayList<>();
  //......

  // 通过 FlutterEngineGroup 实例创立一个 FlutterEngine,第一个 FlutterEngine 和一般创立没区别,之后的就有区别了。public FlutterEngine createAndRunEngine(@NonNull Context context, @Nullable DartEntrypoint dartEntrypoint) {
    FlutterEngine engine = null;
    //......
    if (activeEngines.size() == 0) {
      // 来自 FlutterEngineGroup 创立的第一个 FlutterEngine,和一般创立没区别。engine = createEngine(context);
      engine.getDartExecutor().executeDartEntrypoint(dartEntrypoint);
    } else {
      // 来自 FlutterEngineGroup 创立的非第一个 FlutterEngine,基于第一个进行 spawn 操作返回。engine = activeEngines.get(0).spawn(context, dartEntrypoint);
    }

    activeEngines.add(engine);
    //......
    return engine;
  }

  FlutterEngine createEngine(Context context) {return new FlutterEngine(context);
  }
}

能够看到,与一般 FlutterEngine 的区别在于 FlutterEngineGroup 的 createAndRunEngine 办法创立会有不同,具体在于,createAndRunEngine 办法创立第一个 FlutterEngine 实例与一般无区别,当创立第二个时会通过第一个 FlutterEngine 实例的 spawn(context, dartEntrypoint) 办法进行创立,所以咱们去看下 FlutterEngine 的 spawn 办法,如下:

FlutterEngine spawn(@NonNull Context context, @NonNull DartEntrypoint dartEntrypoint) {
  //......
  FlutterJNI newFlutterJNI =
      flutterJNI.spawn(dartEntrypoint.dartEntrypointFunctionName, dartEntrypoint.dartEntrypointLibrary);
  return new FlutterEngine(
      context, // Context.
      null, // FlutterLoader. A null value passed here causes the constructor to get it from the
      // FlutterInjector.
      newFlutterJNI); // FlutterJNI.
}

很显著看到 spawn 办法是 FlutterEngine 的 c/c++ 层实现的,咱们不再跟进,能够通过他的正文晓得,这种基于以后 FlutterEngine 创立第二个 FlutterEngine 的形式会通过共享尽可能多的资源以最小化启动提早和内存老本,这也就是官网的解决方案。具体样例 demo 参见官网 github 中 samples 的 add_to_app/multiple_flutters。

总结

能够看到,FlutterEngine、FlutterEngineCache、FlutterEngineGroup 的实质都是 FlutterEngine,多出的只是空间换工夫,共用换空间的机制,其余官网 FlutterEngineGroup 早日稳固正式版。

正文完
 0