美文网首页
Flutter 与 Native以及engine的编译

Flutter 与 Native以及engine的编译

作者: 厨子 | 来源:发表于2019-04-18 17:22 被阅读0次

背景

假设你有两个独立的项目,一个纯 Flutter 项目 A,一个 iOS 项目 B。项目A 开发完后,想集成到 B 中。该怎么做?以及如何自定义 engine?

以真机调试为例

首先编译项目 A

在项目A的目录下,执行

// 默认是 -release
$ flutter build ios

这条命令会借助你本地 Flutter 环境进行 build。所以编译出的 Flutter 版本是和你本地 Flutter 环境版本是一致的

编译后的产物在 .ios 文件夹里。打开这个文件夹,找到 Flutter 文件,它的目录结构大概是这样子的:

我们需要的是 App.frameworkengine(引擎)。dart 代码,包括业务代码,三方 package 代码,它们所依赖的 flutter 框架代码,最终将会编译成 App.framework

把编译好的项目资源合到一起
  1. 打开 .ios 文件夹 --> 找到 Flutter 文件夹 --> 找到 App.framework

  2. 拷贝出 App.framework 再拷贝出和它同级的 flutter_assets 文件 (Flutter V1.2.0 开始 flutter_assets 被包含在了 App.framework里面,所以不需要另外拷贝 flutter_assets 文件)

3. 修改拷贝出的 App.framework 的名字为 FlutterXX.framework (V1.2.0 开始不要修改名字,会报错:Library not loaded: @rpath/App.framework/App)

4. 打开 FlutterXX.framework,修改 App 文件的名字为 FlutterXX

  1. 修改 info.plist 中 Bundle identifier,重命名为和项目 B 类似的 id名称,比如:com.公司名.尾部名字

6. 把 flutter_assets 文件拷贝到 FlutterXX.framework 中

嵌入App.framework
  • 把该 App.framework 放入项目 B 的文件目录
  • 然后嵌入这个 framework ,目的是把它放入 ./Frameworks 文件夹,后面会通过 bundle 去加载它,然后初始化Flutter Dart :
Targets --> Build Phases --> Embed Frameworks , add  FlutterXX.framework

嵌入engine

接下来,需要把 flutter 项目需要的 engine 也放入项目 B

  • 方式一:打开上面 .ios文件目录 --> Flutter --> engine --> Flutter.framework

  • 方式二:打开你本地 Flutter 环境所在文件 --> bin --> cache --> artifacts --> engine --> ios-release(release编译) --> Flutter.framework

我们是用 pod 的方式把 Flutter.framework 集成进入,所以根据上面的文件目录,找到 Flutter.framework, 拷贝到我们的 pod 库。更新 pod 库 就行了。

加载项目B

为了让项目B 生效,首先,要把生成的 App.framework 加载进入项目A

NSString *customBundlePath = [[NSBundle mainBundle] pathForResource:@"App" ofType:@"framework" inDirectory:@"./Frameworks"];

然后初始化项目BFlutter Dart 环境:

NSBundle *customBundle = [NSBundle bundleWithPath:customBundlePath];
FlutterDartProject *project = [FlutterDartProject.alloc initWithPrecompiledDartBundle:customBundle];
    

最后一步,可以加载 flutter 页面了:

FLTViewController *flutterVC = [[FLTViewController alloc] initWithProject:project nibName:nil bundle:customBundle];

自定义engine

如果自定义 engine, 我们参考 官方文档 进行以下流程:

  1. fork https://github.com/flutter/engine 到你的 github 账户

    修改 engine 源码的时机有两个:
    1. fork 后,在你的 github 账户就多了一个对应的项目,然后 clone 它,进行修改等操作
    2. 对 engine 的修改,也可以放到下面的第5步进行
    
  2. 根据官方文档,首先找个合适位置,新建文件夹 engine

  3. engine 文件夹,新建 .gclient 文件并在该文件内写入以下内容:

    solutions = [
      {
        "managed": False,
        "name": "src/flutter",
        "url": "git@github.com:<your_name_here>/engine.git",
        "custom_deps": {},
        "deps_file": "DEPS",
        "safesync_url": "",
      },
    ]
    
  4. 还是在 engine 目录,执行

    $ gclient sync
    
  5. 拉取代码结束后,切换到 src/flutter 目录,并添加 upstream

    $ cd src/flutter
    $ git remote add upstream git@github.com:flutter/engine.git
    $ git fetch upstream
    
  6. engine 修改前要明确知道我们 flutter 项目使用的是哪一版本的 engine 然后针对这一版本进行修改。为了找到对应的版本,在你开发的 flutter 项目下执行:

    $ flutter doctor -v
    

    然后有类似下面的输出:

    [✓] Flutter (Channel unknown, v1.2.0, on Mac OS X 10.14 18A391, locale zh-Hans-CN)
     • Flutter version 1.2.0 at /Users/xx/Documents/iOS/flutter
     • Framework revision 06b979c4d5 (3 months ago), 2019-01-25 14:27:35 -0500
     • Engine revision 36acd02c94
     • Dart version 2.1.1 (build 2.1.1-dev.3.2 f4afaee422)
    

    其中 Engine revision 36acd02c94 就是我们要找到的版本

  7. 找到了版本信息,接下来需要执行代码回退,把 engine 回退到 36acd02c94 。我们回到存放 .gclient 文件的目录,执行:

    $ cd src/flutter
    $ git reset --hard 36acd02c94
    
    ///再次回到 `engine` 根目录,为了再次执行 `gclient sync`
    $ cd ../..
    ///这一步不可缺少。版本回退后,这一步是为了同步该版本所依赖的第三方库版本,比如 `thrid_party` 文件里面的内容。
    $ gclient sync --with_branch_heads --with_tags
    
  8. 现在可以修改 engine 源码了

编译engine

engine 源码做了修改之后,需要重新编译,生成我们自定义的 Flutter.framework 产物

#!env zsh
$ cd src
$ rm -rf ./out/Flutter.framework
// '准备 iOS (device & simulator ) 生成 gen_snapshot (dart 编译器)'
$ ./flutter/tools/gn --ios --runtime-mode release
$ ./flutter/tools/gn --ios --simulator --unoptimized
// 生成i386架构下的gen_snapshot
$ ./flutter/tools/gn --ios --ios-cpu arm --runtime-mode release
$ ./flutter/tools/gn --unoptimized
$ ninja -C out/ios_release
$ ninja -C out/ios_release_arm
$ ninja -C out/ios_debug_sim_unopt
$ ninja -C out/host_debug_unopt
$ cp -r ./out/ios_debug_sim_unopt/Flutter.framework  ./out/Flutter.framework

$ lipo ./out/ios_debug_sim_unopt/Flutter.framework/Flutter ./out/ios_release/Flutter.framework/Flutter ./out/ios_release_arm/Flutter.framework/Flutter -output ./out/Flutter -create

$ rm ./out/Flutter.framework/Flutter
$ mv ./out/Flutter ./out/Flutter.framework/

经过漫长的编译期,最后的产物 Flutter.framework 存放在 out 这个文件夹里面。拷贝出来,放到我们的 pod 库里面。

注意

  1. 如果执行 flutter doctor -v 一直处于:
Waiting for another flutter command to release the startup lock

这个状态需要把 Flutter 环境目录下:bin/cache 里面的 lockfile 删掉。再执行flutter doctor -v

  1. 执行 flutter build ios 的时候,遇到
    The sandbox is not in sync with the Podfile.lock
    解决方法,可以参考我这里的回答:
    github issue

参考

Setting up the Engine development environment
编译engine

相关文章

网友评论

      本文标题:Flutter 与 Native以及engine的编译

      本文链接:https://www.haomeiwen.com/subject/qrhouqtx.html