内容简介:在上一篇文章一开始技术架构的搭建自底向上的。当我做出 Flutter 与 Native 之间的最终我决定开发一款命令行工具。它可以解析 Native 代码中的 API,生成对应的 Dart 代码,再进而支持生成 Flutter Plugin/Package 工程。生成的 Flutter 工程会通过 pub 依赖
在上一篇文章 《告别 Flutter Channel,调用 Native API 仅需一行代码!》 发出后,收到了很多关注。仔细想想,其实不是仅仅只需一行代码的,还需要敲一行 codegen 命令来生成 Dart 代码。这回就简单讲下自动生成代码这块的设计和实现原理。
为何要做代码生成工具
一开始技术架构的搭建自底向上的。当我做出 Flutter 与 Native 之间的 高性能通道 后,自然而然地去想要提升易用性,降低开发者的使用门槛。当你从使用者角度去审视自己的产品时,就会自顶向下去设计一些 Feature 去满足目标用户的诉求。
最终我决定开发一款命令行工具。它可以解析 Native 代码中的 API,生成对应的 Dart 代码,再进而支持生成 Flutter Plugin/Package 工程。生成的 Flutter 工程会通过 pub 依赖 DartNative :
工具需要满足以下需求:
- 兼容性:能够支持 Native 多种语言代码的转换,如 Objective-C,Java,Swift,Kotlin 等
- 易用性:开发者能够很方便地安装和使用工具
技术方案
为了满足兼容性,在实现『解析代码生成 AST』的技术方案上,是基于第三方开源框架 antlr 来实现。命令行 工具 使用 NodeJS 实现,开发者可以通过 npm 很方便地安装工具。
如果基于 Clang 来实现的话,性能和稳定性都有保证,但是需要 link 到所需的代码或 framework。且 Clang 对多语言的支持比较局限,也不方便提供给开发者去安装。
关于 antlr 的使用,这里不再赘述,可以直接查看官方文档。(毕竟作者是靠卖书赚钱的)。这里需要将源语言的 grammer 生成 JS 版本的 Runtime 文件,在遍历 AST 的 callback 中收集所需的元数据,转换成为生成 Dart 自定义格式的 AST。最后遍历 Dart AST,生成 Dart 代码。
这里的方案概括为如下步骤:
- 查找出包含客户端 API 的 Native 代码文件,如
.h
和.java
文件 - 通过 antlr parser 生成 Native 语言的 AST
- 将 Native AST 转换成 Dart 语言所需的 AST
- 通过 Dart AST 生成 dart 代码
遇到的坑
词法分析失败
官方提供的 Objective-C 的 grammer 有很多问题,生成的 lexer 还是 parser 都会在词法分析阶段就抛异常。这里就比较坑了,需要修改 lexer 和 parser 的 g4 文件。可能是 grammer 太久没更新了,很多分词阶段就抛异常了,比如 @import
都不支持,真是一言难尽。而且这种只针对单个文件的词法分析程序,很难像 Clang 那样可以 link 其他文件做到的严谨性。经过一系列的修复工作,已经可以 parse iOS Foundation 库的所有头文件。
语法特性映射
这一步发生在 AST 的 transform。虽说不同语言之间有很多语法设计都是想通的,但是依然会有一些难以映射的语法特性。比如 Java 的重载方法,OC 奇葩的方法名。转换成 Dart 的方法命名应该遵循哪方的语言规范?OC 的 Protocol
在 Dart 中如何表示?Dart 类的静态方法不会被继承,那么 Protocol
中的类方法怎么办?Dart 的 enum
不支持自定义 int
值,来自 OC 的 enum
如何转换成 Dart?区分何时生成 Dart 的 import
或 export
?。。。
类似这些操蛋的问题数不胜数。。。需要不断细化,思考,打磨。。。
批量处理
当要处理的文件有很多时,一个一个地串行处理显然会让开发者等得不耐烦。NodeJS 本来不是为 CPU 密集型操作服务的,但是在 v10.5.0 引入 worker_threads 后,实现了真·多线程,解决了 CPU 密集型操作的痛点。
在处理一些稍大点的文件时,需要注意上调 NodeJS VM 老生代内存的阈值。
多种使用场景
考虑到不同的使用场景, codegen 所生成的策略也会不一样:
- 将 App 的 Native 代码转为 Dart 代码,直接在 Flutter 中使用
- 将 Native 系统库转成 Flutter Package
- 将 Native 第三方库转成 Flutter Plugin
这三种场景的共同点:如果 Native 代码依赖了其他库,也需要向 pubspec.yaml 中插入这个依赖库的 Dart 版本。
第 2 和 3 个场景需要用 flutter create
命令生成新的 Flutter 工程,并将生成的 Dart 代码挪到工程里。
更进一步是将生成代码所需的 Native 文件也挪到 Flutter 工程里,并更新 podspec 和 gradle (PS:待实现)。
总结
在做 codegen 之前,我一直觉得有 dart_native 超级通道就足够了。毕竟是自己开发的轮子自己熟悉,认为手写一层 Dart Wrapper 也没啥麻烦的,其实并不然。当给未接触过的开发者使用后,的确是有上手门槛和开发量的。在调研的过程中也发现 bang 神的 JSPatch 也有个 Converter 工具用来把 OC 代码转为 JS 代码,同样用的也是 antlr 解析 AST,在此也十分感谢 JSPatch 提供的思路。超级通道加工具辅助方可实现了 运行性能和开发效率的双提升 。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Simulink自动生成代码
- changelog 日志自动生成插件
- APIAuto 2.0.0 发布,机器学习自动化测试、自动生成代码和注释、自动静态检查...
- 深度有趣 | 23 歌词古诗自动生成
- 测者的测试技术手册:自动的自动化EvoSuite 自动生成JUnit的测试用例
- 数据生成工具 ZenData 发布 2.0 版本,自动生成“互联网黑话”!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Mobile Web
Lyza Danger Gardner、Jason Grigsby / O'Reilly Media / 2011-12 / $ 50.84
Despite the huge number of mobile devices and apps in use today, your business still needs a website. You just need it to be mobile. Head First Mobile Web walks you through the process of making a con......一起来看看 《Head First Mobile Web》 这本书的介绍吧!