flutter架构(4):平台嵌入混编

flutter架构(1):概述
flutter架构(2):Widget
flutter架构(3):渲染和布局
flutter架构(5):web支持

Platform embedding --平台嵌入混编

As we’ve seen, rather than being translated into the equivalent OS widgets, Flutter user interfaces are built, laid out, composited, and painted by Flutter itself. The mechanism for obtaining the texture and participating in the app lifecycle of the underlying operating system inevitably varies depending on the unique concerns of that platform. The engine is platform-agnostic, presenting a stable ABI (Application Binary Interface) that provides a platform embedder with a way to set up and use Flutter.

如我们所见,Flutter用户界面不是转换成等效的OS widget, 而是由Flutter自身build,layout,组合和paint。 “获取纹理”并且参“与基础操作系统的app生命周期”的机制不可避免地会根据该平台的特性而变化。 Flutter引擎做到平台无关,它提供了一个平台embedder程序–稳定ABI(app二进制接口),通过ABI,平台可以设置和使用Flutter。

The platform embedder is the native OS application that hosts all Flutter content, and acts as the glue between the host operating system and Flutter. When you start a Flutter app, the embedder provides the entrypoint, initializes the Flutter engine, obtains threads for UI and rastering, and creates a texture that Flutter can write to. The embedder is also responsible for the app lifecycle, including input gestures (such as mouse, keyboard, touch), window sizing, thread management, and platform messages. Flutter includes platform embedders for Android, iOS, Windows, macOS, and Linux; you can also create a custom platform embedder, as in this worked example that supports remoting Flutter sessions through a VNC-style framebuffer or this worked example for Raspberry Pi.

平台embedder是承载所有Flutter内容的native OS app,并且充当主机操作系统和Flutter之间的粘合剂。 启动Flutter app时,embedder程序将提供入口点,初始化Flutter引擎,获取用于UI和栅格化的线程,并创建Flutter可以写入的纹理。 embedder还负责应用程序的生命周期,包括输入手势(例如鼠标,键盘,触摸),窗口大小,线程管理和平台消息。 Flutter包含适配embedder了的平台有:Android,iOS,Windows,macOS和Linux等; 你还可以创建一个自定义平台embedder程序,如this worked example 中所示,它支持通过VNC样式的帧缓冲区或this worked example for Raspberry Pi

Each platform has its own set of APIs and constraints. Some brief platform-specific notes:
每个平台都有自己的一组API和限制。 一些特定于平台的简短说明:

  1. On iOS and macOS, Flutter is loaded into the embedder as a UIViewController or NSViewController, respectively. The platform embedder creates a FlutterEngine, which serves as a host to the Dart VM and your Flutter runtime, and a FlutterViewController, which attaches to the FlutterEngine to pass UIKit or Cocoa input events into Flutter and to display frames rendered by the FlutterEngine using Metal or OpenGL.
    在iOS和macOS上,Flutter分别作为UIViewControllerNSViewController加载到embedder中。 平台embedder程序创建一个FlutterEngine(作为Dart VM和你的Flutter运行时的主机),以及一个FlutterViewController(attache到FlutterEngine),以将UIKit或Cocoa输入事件传递到Flutter,FlutterEngine使用Metal或OpenGL显示渲染的帧。

  2. On Android, Flutter is, by default, loaded into the embedder as an Activity. The view is controlled by a FlutterView, which renders Flutter content either as a view or a texture, depending on the composition and z-ordering requirements of the Flutter content.
    在Android上,默认情况下,Flutter作为Activity加载到embedder中。 View由FlutterView进行控制,FlutterView将Flutter内容呈现为view或纹理(texture),具体取决于Flutter内容的组合z-ordering

  3. On Windows, Flutter is hosted in a traditional Win32 app, and content is rendered using ANGLE, a library that translates OpenGL API calls to the DirectX 11 equivalents. Efforts are currently underway to also offer a Windows embedder using the UWP app model, as well as to replace ANGLE with a more direct path to the GPU via DirectX 12.
    在Windows上,Flutter托管在传统的Win32应用程序中,并且使用ANGLE(一个将OpenGL API调用转换为DirectX 11等效项的库)呈现内容。 当前正在努力使用UWP应用程序模型提供Windows embedder程序,并通过DirectX 12以更直接的方式将ANGLE替换为GPU。

Integrating with other code --与其他代码集成

Flutter provides a variety of interoperability mechanisms, whether you’re accessing code or APIs written in a language like Kotlin or Swift, calling a native C-based API, embedding native controls in a Flutter app, or embedding Flutter in an existing application.
Flutter提供了多种互操作性机制,无论是访问以Kotlin或Swift之类的语言编写的代码或API,调用基于C的 native API,将native控件嵌入Flutter应用程序中,还是将Flutter嵌入现有应用程序中。

Platform channels

For mobile and desktop apps, Flutter allows you to call into custom code through a platform channel, which is a simple mechanism for communicating between your Dart code and the platform-specific code of your host app. By creating a common channel (encapsulating a name and a codec), you can send and receive messages between Dart and a platform component written in a language like Kotlin or Swift. Data is serialized from a Dart type like Map into a standard format, and then deserialized into an equivalent representation in Kotlin (such as HashMap) or Swift (such as Dictionary).

对于移动和桌面app,Flutter允许你通过 platform channel调用自定义代码,这是在Dart代码与宿主app的平台特定代码之间进行通信的一种简单机制。 通过创建公共channel(封装名称和编解码器),您可以在Dart与以Kotlin或Swift之类的语言编写的平台组件之间发送和接收消息。 数据从诸如Map的Dart类型序列化为标准格式,然后反序列化为Kotlin(例如HashMap)或Swift(例如Dictionary)的等效表示形式。
在这里插入图片描述

The following is a simple platform channel example of a Dart call to a receiving event handler in Kotlin (Android) or Swift (iOS):
以下是简单platform channel 示例:在Kotlin(Android)或Swift(iOS)中Dart调用接收事件进行处理:

// Dart side
const channel = MethodChannel('foo');
final String greeting = await channel.invokeMethod('bar', 'world');
print(greeting);
// Android (Kotlin)
val channel = MethodChannel(flutterView, "foo")
channel.setMethodCallHandler { call, result ->
  when (call.method) {
    "bar" -> result.success("Hello, ${call.arguments}")
    else -> result.notImplemented()
  }
}
// iOS (Swift)
let channel = FlutterMethodChannel(name: "foo", binaryMessenger: flutterView)
channel.setMethodCallHandler {
  (call: FlutterMethodCall, result: FlutterResult) -> Void in
  switch (call.method) {
    case "bar": result("Hello, \(call.arguments as! String)")
    default: result(FlutterMethodNotImplemented)
  }
}

Further examples of using platform channels, including examples for macOS, can be found in the flutter/plugins repository. There are also thousands of plugins already available for Flutter that cover many common scenarios, ranging from Firebase to ads to device hardware like camera and Bluetooth.
可以在 flutter/plugins repository中找到使用platform channel的更多示例,包括macOS的示例。 Flutter也有 成千上万的插件可用 ,里面涵盖了许多常见的场景,从Firebase到广告再到相机和蓝牙等设备硬件。

Foreign Function Interface --外围功能功能

For C-based APIs, including those that can be generated for code written in modern languages like Rust or Go, Dart provides a direct mechanism for binding to native code using the dart:ffi library. The foreign function interface (FFI) model can be considerably faster than platform channels, because no serialization is required to pass data. Instead, the Dart runtime provides the ability to allocate memory on the heap that is backed by a Dart object and make calls to statically or dynamically linked libraries. FFI is available for all platforms other than web, where the js package serves an equivalent purpose.

对于基于C的API,包括用Rust或Go等现代语言编写的代码生成的API,Dart提供了一种直接的机制,用于使用dart:ffi 库绑定到本机代码。 外围功能功能(FFI)模型可以比platform channels快得多,因为不需要序列化即可传递数据。 相反,Dart runtime提供了在Dart对象支持的堆上分配内存并调用静态或动态链接库的功能。 FFI适用于除Web之外的所有平台,Web的 js package 具有相同的用途。

To use FFI, you create a typedef for each of the Dart and unmanaged method signatures, and instruct the Dart VM to map between them. As a simple example, here’s a fragment of code to call the traditional Win32 MessageBox() API:
要使用FFI,请为每个Dart和非托管方法签名创建一个typedef,并指示Dart VM在它们之间进行映射。 举一个简单的例子,下面是一段代码片段,用于调用传统的Win32 MessageBox() API:

typedef MessageBoxNative = Int32 Function(
    IntPtr hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, Int32 uType);
typedef MessageBoxDart = int Function(
    int hWnd, Pointer<Utf16> lpText, Pointer<Utf16> lpCaption, int uType);

final user32 = DynamicLibrary.open('user32.dll');
final MessageBox =
    user32.lookupFunction<MessageBoxNative, MessageBoxDart>('MessageBoxW');

final result = MessageBox(
    0, // No owner window
    Utf16.toUtf16('Test message'),   // Message
    Utf16.toUtf16('Window caption'), // Window title
    0 // OK button only
    );

Rendering native controls in a Flutter app --在Flutter应用中渲染native控件

Because Flutter content is drawn to a texture and its widget tree is entirely internal, there’s no place for something like an Android view to exist within Flutter’s internal model or render interleaved within Flutter widgets. That’s a problem for developers that would like to include existing platform components in their Flutter apps, such as a browser control.
由于Flutter的内容被绘制到纹理上,并且它的widget树完全在内部,因此在Flutter的内部模型中无法存在Android View之类的内容,而在Flutter的widget中则无法交错显示。 对于想要在Flutter应用程序中包含现有平台组件(例如浏览器控件)的开发人员来说,这是一个问题。

Flutter solves this by introducing platform view widgets (AndroidView and UiKitView) that let you embed this kind of content on each platform. Platform views can be integrated with other Flutter content. Each of these widgets acts as an intermediary to the underlying operating system. For example, on Android,AndroidView serves three primary functions:

Flutter通过引入platform view widget(AndroidView和UiKitView)解决了这一问题,您可以在每个平台上混编这类内容(附:这种方法有一些局限性,例如,透明度对于platform view的组合方式与其他Flutter widget的组合方式不同。)。 platform view 可以与其他Flutter内容集成。 这些widget中的每一个都充当基础操作系统的中介桥梁。 例如,在Android上,AndroidView具有三个主要功能:

  1. Making a copy of the graphics texture rendered by the native view and presenting it to Flutter for composition as part of a Flutter-rendered surface each time the frame is painted.
    复制native view渲染的图形纹理,并在绘制每一帧时将其呈现给Flutter,以作为Flutter渲染surface中的一部分。
  2. Responding to hit testing and input gestures, and translating those into the equivalent native input.
    响应hit test和输入手势,并将其转换为等效的native输入。
  3. Creating an analog of the accessibility tree, and passing commands and responses between the native and Flutter layers.
    Inevitably, there is a certain amount of overhead associated with this synchronization. In general, therefore, this approach is best suited for complex controls like Google Maps where reimplementing in Flutter isn’t practical.
    创建类似的可访问性树,并在native层和Flutter层之间传递命令和响应。
    不可避免地,与此同步相关联有一定的开销成本。 因此,通常,这种方法最适合于复杂的控件,例如Google地图,这种在Flutter中不可能重新实现一套。

Typically, a Flutter app instantiates these widgets in a build() method based on a platform test. As an example, from the google_maps_flutter plugin:
通常,Flutter app会基于平台测试在build()方法中实例化这些小部件。 例如,通过google_maps_flutter 插件:

if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(
        viewType: 'plugins.flutter.io/google_maps',
        onPlatformViewCreated: onPlatformViewCreated,
        gestureRecognizers: gestureRecognizers,
        creationParams: creationParams,
        creationParamsCodec: const StandardMessageCodec(),
      );
    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
      return UiKitView(
        viewType: 'plugins.flutter.io/google_maps',
        onPlatformViewCreated: onPlatformViewCreated,
        gestureRecognizers: gestureRecognizers,
        creationParams: creationParams,
        creationParamsCodec: const StandardMessageCodec(),
      );
    }
    return Text(
        '$defaultTargetPlatform is not yet supported by the maps plugin');
  }

Communicating with the native code underlying the AndroidView or UiKitView typically occurs using the platform channels mechanism, as previously described.
如前所述,通常使用platform channel机制与AndroidViewUiKitView基础的native代码进行通信。

At present, platform views aren’t available for desktop platforms, but this is not an architectural limitation; support might be added in the future.
目前,platform view不适用于桌面平台,但这不是体系结构上的限制; 将来可能会增加支持。

Hosting Flutter content in a parent app --在现有app中接入Flutter

The converse of the preceding scenario is embedding a Flutter widget in an existing Android or iOS app. As described in an earlier section, a newly created Flutter app running on a mobile device is hosted in an Android activity or iOS UIViewController. Flutter content can be embedded into an existing Android or iOS app using the same embedding API.
与上述场景相反的是将Flutter widget 混编嵌入现有的Android或iOS app中。 如前一节所述,在移动设备上运行的新创建的Flutter app托管在Android Activity或iOS UIViewController中。 可以使用相同的嵌入API将Flutter内容嵌入到现有的Android或iOS应用中。

The Flutter module template is designed for easy embedding; you can either embed it as a source dependency into an existing Gradle or Xcode build definition, or you can compile it into an Android Archive or iOS Framework binary for use without requiring every developer to have Flutter installed.
Flutter module模板设计成易于嵌入; 你可以将其作为源依赖项嵌入到现有的Gradle或Xcode build definition中,也可以将其编译为Android Archive或iOS Framework二进制文件以供使用,而无需每个开发人员都安装Flutter。

The Flutter engine takes a short while to initialize, because it needs to load Flutter shared libraries, initialize the Dart runtime, create and run a Dart isolate, and attach a rendering surface to the UI. To minimize any UI delays when presenting Flutter content, it’s best to initialize the Flutter engine during the overall app initialization sequence, or at least ahead of the first Flutter screen, so that users don’t experience a sudden pause while the first Flutter code is loaded. In addition, separating the Flutter engine allows it to be reused across multiple Flutter screens and share the memory overhead involved with loading the necessary libraries.
Flutter引擎需要很短的时间来初始化,因为它需要加载Flutter共享库,初始化Dart runtime,创建并运行Dart isolate,并将渲染图面attach到UI。 为了最大程度地减少显示Flutter内容时的UI延迟,最好在整个app初始化过程中或至少在第一个Flutter屏幕之前初始化Flutter engine,以使用户在载入第一个Flutter代码时不会突然停顿。 此外,将Flutter engine分开可以使其在多个Flutter屏幕上重复使用,并共享与加载必要库有关的内存开销。

More information about how Flutter is loaded into an existing Android or iOS app can be found at the Load sequence, performance and memory topic.

有关如何将Flutter加载到现有Android或iOS应用程序的更多信息,请参见Flutter的加载流程,性能,内存情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值