从零开始|构建 Flutter 多引擎渲染组件:先导篇

770 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情

《Flutter 多引擎渲染,在稿定 App 的实践》 等介绍篇章,本专栏里可查看

前言

前面的篇章讲了笔者用 Flutter 多引擎渲染做到了什么程度,以及在过程中做了哪些探索和实践。但对于初入门的同学来讲并不算友好,没有太多的细节介绍如何开发、如何链接多端、如何贯通开发流程。

会包含以下部分:

  • Flutter 多引擎渲染工作原理
  • 如何从零开始搭建 Flutter 多引擎渲染组件应用在 iOS、Android 项目中
  • 如何构建跨端工具链来优雅的抹平开发构建成本

Component API.png

围绕上图的 FGUIComponentAPI 实现过程展开。

特别说明

这一系列的目的是从零开始让大家能真正实操到项目中,适用于有语言基础(dart、objective-c、kotlin、java、ruby/python)的同学,所以除了各种语言的基础语法不会多做解释,其他都会逐步讲解,也会尽可能增加每行代码的注释。

但由于工作上事情也蛮多,所以会在12月把本系列的文章全部发出来给大家参考(尽量不鸽)。

读者将会本文中得到:

  1. 如何搭建的开发环境
  2. 须知的官网 Demo 及关键代码解释

环境搭建

从零开始,那就要从环境搭建开始。其实多引擎开发上并不需要什么特别的配置,flutter doctor 都能过就可以了,这里简单的写一下搭建过程,且只写笔者最推荐的方式。

以 Mac 电脑为例,Windows 已经很久没用过了,也不是很清楚要如何配置,具体的就 Flutter 官网看一下。

iOS 环境

iOS 集成很高,不用特别的操作,App Store 里搜一下 xcode 安装即可,现在膨胀到 10+G 大了,需要耐心的等

Ruby 安装

虽然 mac 都自带 Ruby,但系统的 Ruby 有诸多限制,建议还是装一个开发使用的 Ruby 环境。

也是通过 brew 安装:

brew install ruby

同样也需要配置下环境变量

echo 'export PATH="/usr/local/opt/ruby/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

安装完成后,执行下 which -a rubyruby -v 验证下

image.png

Android 环境

Android 麻烦一些,且准备好科学上网工具,google 不是你想访问就能访问的。

Android Studio

Android 开发 IDE,传送门:developer.android.google.cn/studio

安装完成后,各种 SDK Manager 需要装一下

image.png

全局环境变量也需要配置:

export 
export ANDROID_HOME=/Users/yuanxiao/Documents/sdk/android
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
export PATH=${PATH}:{ANDROID_HOME}/tools

Flutter 环境

Flutter 版本管理

强烈推荐使用 Flutter 版本管理工具:fvm 来管理 Flutter SDK,可以随时切换多个 Flutter 版本来进行开发。这点在 Flutter 开发上很有用,毕竟一直跟最新版当小白鼠风险很大,但还是要升级看看到底有没有风险[手动狗头]。

fvm 安装

brew tap leoafarias/fvm

brew install fvm

如果没有装 homebrew 的话,这里建议装一下 - -。

fvm 常用命令

fvm list 列出当前安装的 Flutter 版本列表

fvm global 全局切换版本至

fvm use 当前项目切换版本至

image.png

fvm install x.x.x 安装某一个版本的 Flutter

image.png

十分方便

配置环境变量

当然为了项目外也能使用,还是要配置下环境变量:

打开(或创建) $HOME/.bash_profile

增加 PATH 路径

export PATH=$HOME/fvm/default/bin:$PATH
export PATH=$HOME/fvm/default/bin/cache/dart-sdk/bin:$PATH

开发工具

Flutter 开发建议使用: VS Code

调试环境建议使用:Chrome (Web 开发调试)

工具链脚本开发

在后续篇章,我们会讲到跨端工具链的研发。如果对 Ruby 不算熟,也可以选择自己熟悉的脚本语言:Node、Python 等,但不建议用 dart 脚本,性能完全不行。

我们用到的脚本能力十分基础,具备文件操作即可。

环境测试

执行 flutter doctor

image.png

全对就 OK 了,至此整套开发环境已齐备。

官方源码

在讲我们如何实现多引擎渲染组件之前,建议大家都去了解一下官方提供的多引擎 Demo,比较简单易懂。

multiple_flutters

传送门:github.com/flutter/sam…

它的源码还是很简单的

关键代码解释

抛开 Channel 部分,因为我们也不会这么用它

Flutter 侧

@pragma('vm:entry-point')
// 这句是让 Flutter build 时要保留住,不不要被 treeshake 掉
void topMain() => runApp(const MyApp(color: Colors.green)); 
// runApp 跟 main() 的 runApp 没区别,但 MyApp 不能是一个 MaterialApp

@pragma('vm:entry-point')
void bottomMain() => runApp(const MyApp(color: Colors.purple));

topMain() 和 bottomMain() 相当于暴露给 Native 侧直接调用,原则上不能通过这里传递参数的(但有些厂也喜欢魔改一下,让这个方法可以传参)。

iOS 侧

iOS 侧的 Demo 有些差强人意,我把关键的代码从几个文件里抽出来介绍。


// 创建一个 FlutterEngineGroup,multiple-flutters 会是你的线程名称
let engines = FlutterEngineGroup(name: "multiple-flutters", project: nil)

// 从中创建出一个 FlutterEngine
// 其中 entryPoint = "topMain" / "bottomMain" 就是 flutter 里定义的暴露层,但这个有坑,先 mark
let newEngine = appDelegate.engines.makeEngine(withEntrypoint: entryPoint, libraryURI: nil)

// 完成 FlutterViewController 初始化,这样完成 FlutterEngine 与 VC 的绑定,页面上就可以显示出 VC.view
super.init(engine: newEngine, nibName: nil, bundle: nil)

Android 侧

也是差强人意,但其实和 iOS 一样,关键的也是这个过程代码

// 创建 FlutterEngineGroup
engines = FlutterEngineGroup(this)

// 从中创建出一个 FlutterEngine
// dartEntrypoint 就是 "topMain" / "bottomMain"
engine = app.engines.createAndRunEngine(activity, dartEntrypoint)

// 重写 provideFlutterEngine,完成 FlutterActivity 和 FlutterEngine 的绑定
override fun provideFlutterEngine(context: Context): FlutterEngine? {
    return engineBindings.engine
}

pigeon

还要看一下 pigeon 的 demo,我们后面也是会集成 pigeon 到项目中。

传送门:

github.com/flutter/sam…

pigeon 的例子叫 books [滑稽]

它是一个跨端工具,让 dart 代码生成 iOS、Android 代码。

如下图位置,schema.dart 是源文件

image.png
// 定义一个模型
class Book {
  // 要注意都必须是可为空的对象
  String? title;
  String? subtitle;
  String? author;
  String? summary;
  String? publishDate;
  int? pageCount;
  // 模型可嵌套,也可以定义 List<Thumbnail?>?
  Thumbnail? thumbnail;
}

class Thumbnail {
  String? url;
}

// Native 调用 Flutter 增加 @FlutterApi() 
// 这里只能是抽象类,且里面只能写方法,不能写别的
@FlutterApi()
abstract class FlutterBookApi {
  void displayBookDetails(Book book);
}

// Flutter 调用 Native 增加 @HostApi() 
@HostApi()
abstract class HostBookApi {
  void cancel();
  void finishEditingBook(Book book);
}

实际使用上限制非常多,且不能引用任何其他库,包括 Flutter 自身的库。

Flutter 生成在 flutter_module_books/lib/Api.dart

iOS 生成在 ios_books/IosBooks/api.h

Android 生成在 android_books/app/src/main/java/dev/flutter/example/books/Api.java

脚本

再说一下为什么会生成在这些位置上,怎么做到的,其实是执行了一段脚本:run_pigeon.sh

#!/bin/sh
flutter pub run pigeon --input pigeon/schema.dart \
  --dart_out lib/api.dart \
  --objc_header_out ../ios_books/IosBooks/api.h \
  --objc_source_out ../ios_books/IosBooks/api.m \
  --objc_prefix BK \
  --java_out ../android_books/app/src/main/java/dev/flutter/example/books/Api.java \
  --java_package "dev.flutter.example.books"

--input 解析的源文件

--dart_out dart 文件输出位置

--objc_header_out / --objc_source_out iOS 代码输出位置

--objc_prefix OC 代码因为没有命名空间,所以需要增加的前缀

--java_out Android 代码输出位置

--java_package Java 包名

这实质上调用的是 flutter pub run pigeon,dart 脚本执行效率确实很堪忧。

传送门

《从零开始|构建 Flutter 多引擎渲染组件:Flutter 工程篇》

《从零开始|构建 Flutter 多引擎渲染组件:Flutter 代码篇》


感谢阅读,如果对你有用请点个赞 ❤️